001: /*
002: * Copyright 2005-2007 Noelios Consulting.
003: *
004: * The contents of this file are subject to the terms of the Common Development
005: * and Distribution License (the "License"). You may not use this file except in
006: * compliance with the License.
007: *
008: * You can obtain a copy of the license at
009: * http://www.opensource.org/licenses/cddl1.txt See the License for the specific
010: * language governing permissions and limitations under the License.
011: *
012: * When distributing Covered Code, include this CDDL HEADER in each file and
013: * include the License file at http://www.opensource.org/licenses/cddl1.txt If
014: * applicable, add the following below this CDDL HEADER, with the fields
015: * enclosed by brackets "[]" replaced with your own identifying information:
016: * Portions Copyright [yyyy] [name of copyright owner]
017: */
018:
019: package com.noelios.restlet.local;
020:
021: import java.io.File;
022: import java.io.IOException;
023: import java.util.ArrayList;
024: import java.util.Enumeration;
025: import java.util.List;
026: import java.util.jar.JarEntry;
027: import java.util.jar.JarFile;
028: import java.util.logging.Level;
029:
030: import org.restlet.Client;
031: import org.restlet.data.Protocol;
032: import org.restlet.data.Reference;
033: import org.restlet.data.ReferenceList;
034: import org.restlet.data.Request;
035: import org.restlet.data.Response;
036: import org.restlet.data.Status;
037: import org.restlet.resource.InputRepresentation;
038: import org.restlet.resource.Representation;
039:
040: /**
041: * Connector to the WAR resources. Here is the list of parameters that are
042: * supported: <table>
043: * <tr>
044: * <td>warPath</td>
045: * <td>String</td>
046: * <td>${user.home}/restlet.war</td>
047: * <td>Path to the Web Application WAR file or directory.</td>
048: * </tr>
049: * </table>
050: *
051: * @author Jerome Louvel (contact@noelios.com)
052: */
053: public class WarClientHelper extends FileClientHelper {
054: /**
055: * Restrict the access to the META-INF and WEB-INF directories. False by
056: * default.
057: */
058: private boolean restrict;
059:
060: /** The location of the Web Application archive file or directory path. */
061: private String warPath;
062:
063: /**
064: * Indicates if the Web Application path corresponds to an archive file or a
065: * directory path.
066: */
067: private boolean webAppArchive;
068:
069: /** Cache of all the WAR file entries to improve directory listing time. */
070: private List<String> warEntries;
071:
072: /**
073: * Constructor. Note that the common list of metadata associations based on
074: * extensions is added, see the addCommonExtensions() method.
075: *
076: * @param client
077: * The client to help.
078: */
079: public WarClientHelper(Client client) {
080: super (client);
081: getProtocols().clear();
082: getProtocols().add(Protocol.WAR);
083: this .restrict = false;
084: this .warPath = null;
085: this .webAppArchive = false;
086: this .warEntries = null;
087: }
088:
089: /**
090: * Handles a call.
091: *
092: * @param request
093: * The request to handle.
094: * @param response
095: * The response to update.
096: */
097: public void handle(Request request, Response response) {
098: String scheme = request.getResourceRef().getScheme();
099:
100: // Ensure that all ".." and "." are normalized into the path
101: // to preven unauthorized access to user directories.
102: request.getResourceRef().normalize();
103:
104: if (scheme.equalsIgnoreCase("war")) {
105: handleWar(request, response);
106: } else {
107: throw new IllegalArgumentException(
108: "Protocol \""
109: + scheme
110: + "\" not supported by the connector. Only WAR is supported.");
111: }
112: }
113:
114: /**
115: * Handles a call using the current Web Application.
116: *
117: * @param request
118: * The request to handle.
119: * @param response
120: * The response to update.
121: */
122: protected void handleWar(Request request, Response response) {
123: getWarPath();
124:
125: if (this .webAppArchive) {
126: try {
127: String path = request.getResourceRef().getPath();
128: JarFile war = new JarFile(getWarPath());
129: // As the path may be percent-encoded, it has to be percent-decoded.
130: // Prepare a jar URI, removing the leading slash
131: if ((path != null) && path.startsWith("/"))
132: path = path.substring(1);
133: JarEntry entry = war
134: .getJarEntry(Reference.decode(path));
135:
136: if (entry.isDirectory()) {
137: if (warEntries == null) {
138: // Cache of all the WAR file entries to improve
139: // directory listing time.
140: warEntries = new ArrayList<String>();
141: for (Enumeration<JarEntry> entries = war
142: .entries(); entries.hasMoreElements();) {
143: warEntries.add(entries.nextElement()
144: .getName());
145: }
146: }
147:
148: // Return the directory listing
149: ReferenceList rl = new ReferenceList();
150: rl.setIdentifier(request.getResourceRef());
151:
152: for (String warEntry : warEntries) {
153: if (warEntry.startsWith(path)) {
154: rl.add(new Reference(warEntry));
155: }
156: }
157:
158: response.setEntity(rl.getTextRepresentation());
159: response.setStatus(Status.SUCCESS_OK);
160: } else {
161: // Return the file content
162: Representation output = new InputRepresentation(war
163: .getInputStream(entry), null);
164: updateMetadata(getMetadataService(request), path,
165: output);
166: output.setIdentifier(request.getResourceRef());
167: response.setEntity(output);
168: response.setStatus(Status.SUCCESS_OK);
169: }
170: } catch (IOException e) {
171: getLogger().log(Level.WARNING,
172: "Unable to access to the WAR file", e);
173: response.setStatus(Status.SERVER_ERROR_INTERNAL);
174: }
175:
176: } else {
177: String path = request.getResourceRef().getPath();
178:
179: if (isRestrict()
180: && path.toUpperCase().startsWith("/WEB-INF/")) {
181: getLogger().warning(
182: "Forbidden access to the WEB-INF directory detected. Path requested: "
183: + path);
184: response.setStatus(Status.CLIENT_ERROR_NOT_FOUND);
185: } else if (isRestrict()
186: && path.toUpperCase().startsWith("/META-INF/")) {
187: getLogger().warning(
188: "Forbidden access to the META-INF directory detected. Path requested: "
189: + path);
190: response.setStatus(Status.CLIENT_ERROR_NOT_FOUND);
191: } else {
192: path = getWarPath() + path;
193: handleFile(request, response, path);
194: }
195: }
196: }
197:
198: /**
199: * Returns the Web Application archive file or directory path.
200: *
201: * @return The Web Application archive file or directory path.
202: */
203: public String getWarPath() {
204: if (this .warPath == null) {
205: this .warPath = getParameters().getFirstValue(
206: "warPath",
207: System.getProperty("user.home") + File.separator
208: + "restlet.war");
209: File file = new File(this .warPath);
210:
211: if (file.exists()) {
212: if (file.isDirectory()) {
213: this .webAppArchive = false;
214:
215: // Adjust the archive directory path if necessary
216: if (warPath.endsWith("/"))
217: this .warPath = this .warPath.substring(0,
218: this .warPath.length() - 1);
219: } else {
220: this .webAppArchive = true;
221: }
222: } else {
223: getLogger().warning(
224: "Unable to find an existing directory or archive at: "
225: + this .warPath);
226: }
227: }
228:
229: return this .warPath;
230: }
231:
232: /**
233: * Indicates if the access to the META-INF and WEB-INF directories is
234: * restricted. False by default.
235: *
236: * @return True if the access is restricted.
237: */
238: public boolean isRestrict() {
239: return this .restrict;
240: }
241:
242: /**
243: * Indicates if the access to the META-INF and WEB-INF directories is
244: * restricted.
245: *
246: * @param restrict
247: * True if the access is restricted.
248: */
249: public void setRestrict(boolean restrict) {
250: this.restrict = restrict;
251: }
252:
253: }
|