001: /*
002: * Copyright (C) 2006 Methodhead Software LLC. All rights reserved.
003: *
004: * This file is part of TransferCM.
005: *
006: * TransferCM is free software; you can redistribute it and/or modify it under the
007: * terms of the GNU General Public License as published by the Free Software
008: * Foundation; either version 2 of the License, or (at your option) any later
009: * version.
010: *
011: * TransferCM is distributed in the hope that it will be useful, but WITHOUT ANY
012: * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
013: * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
014: * details.
015: *
016: * You should have received a copy of the GNU General Public License along with
017: * TransferCM; if not, write to the Free Software Foundation, Inc., 51 Franklin St,
018: * Fifth Floor, Boston, MA 02110-1301 USA
019: */
020:
021: package com.methodhead.res;
022:
023: import org.apache.commons.io.FileUtils;
024: import java.io.File;
025: import java.io.IOException;
026: import javax.servlet.http.HttpServlet;
027: import java.util.List;
028: import java.util.Iterator;
029: import com.methodhead.tree.FoldingTreeNode;
030: import javax.servlet.http.HttpServletRequest;
031: import org.apache.struts.action.ActionMapping;
032:
033: /**
034: * A collection of utility methods used in the
035: * <tt>com.methodhead.res</tt> package.
036: */
037: public class ResUtils {
038:
039: // constructors /////////////////////////////////////////////////////////////
040:
041: // constants ////////////////////////////////////////////////////////////////
042:
043: // classes //////////////////////////////////////////////////////////////////
044:
045: // methods //////////////////////////////////////////////////////////////////
046:
047: /**
048: * Returns <tt>true</tt> if <tt>name</tt> has an unsafe file extension
049: * (<tt>.jsp</tt> to <tt>.do</tt>).
050: */
051: public static boolean isSafeFileName(String name) {
052:
053: return !name.toLowerCase().endsWith(".jsp")
054: && !name.toLowerCase().endsWith(".do");
055: }
056:
057: /**
058: * Returns <tt>true</tt> if <tt>name</tt> doesn't contain any invalid
059: * characters (<tt>\</tt>, <tt>/</tt>).
060: */
061: public static boolean isValidFileName(String name) {
062:
063: return (name.indexOf('/') == -1) && (name.indexOf('\\') == -1);
064: }
065:
066: /**
067: * Returns <tt>true</tt> if <tt>path</tt> doesn't contain any invalid
068: * characters sequences (<tt>..</tt>).
069: */
070: public static boolean isValidPath(String path) {
071:
072: if (path == null)
073: return false;
074:
075: return path.indexOf("..") == -1;
076: }
077:
078: /**
079: * Replaces any consecutive file separators with a single file separator.
080: */
081: public static String cleanPath(String path) {
082: return path.replaceAll("" + File.separator + "+",
083: File.separator);
084: }
085:
086: /**
087: * Trims any leading or trailing spaces and separator characters from
088: * <tt>path</tt>.
089: */
090: public static String trimPath(String path) {
091:
092: path = path.trim();
093:
094: if (path.startsWith("/"))
095: path = path.substring(1);
096:
097: if (path.endsWith("/"))
098: path = path.substring(0, path.length() - 1);
099:
100: return path;
101: }
102:
103: /**
104: * Returns <tt>true</tt> if <tt>path</tt> is contained by <tt>parentPath</tt>
105: * at some level. If <tt>path</tt> equals <tt>parentPath</tt> this method
106: * will also return <tt>true</tt>. This method is strictly a string
107: * operation; no files are actually accessed. The path separator is always
108: * assumed to be a forward slash (<tt>/</tt>). Any leading or trailing
109: * spaces and separator characters are trimmed from both paths before any
110: * tests are performed.
111: */
112: public static boolean isPathDescendent(String parentPath,
113: String path) {
114:
115: if ((parentPath == null) || (path == null))
116: return false;
117:
118: parentPath = trimPath(parentPath);
119: path = trimPath(path);
120:
121: if (path.startsWith(parentPath))
122: return true;
123:
124: return false;
125: }
126:
127: /**
128: * Copies <tt>from</tt> to <tt>to</tt>; if <tt>from</tt> is a directory, it
129: * is recursively copied. If <tt>to</tt> is exists and is not a directory,
130: * it is overwritten. If <tt>to</tt> is exists and is a directory, the
131: * contents of <tt>from</tt> are copied over the contents of <tt>to</tt>. If
132: * <tt>to</tt> exists, but is a directory when <tt>from</tt> is not (or vice
133: * versa), an exception is thrown. NOT UNIT TESTED
134: */
135: public static void copyFile(File from, File to)
136: throws ResException, IOException {
137:
138: if (from.isDirectory()) {
139: if (to.exists()) {
140: if (!to.isDirectory())
141: throw new ResException(
142: "Can't copy directory over non-directory: "
143: + to.getAbsolutePath());
144: } else {
145: to.mkdir();
146: }
147:
148: //
149: // recurse into directory
150: //
151: File[] files = from.listFiles();
152:
153: for (int i = 0; i < files.length; i++)
154: copyFile(files[i], new File(to, files[i].getName()));
155: } else {
156: if (to.exists() && to.isDirectory())
157: throw new ResException(
158: "Can't copy non-directory over directory: "
159: + to.getAbsolutePath());
160:
161: FileUtils.copyFile(from, to);
162: }
163: }
164:
165: /**
166: * Deletes <tt>file</tt>; if <tt>file</tt> is a directory, it is recursively
167: * deleted.
168: */
169: public static void deleteFile(File file) {
170:
171: if (file.isDirectory()) {
172:
173: File[] files = file.listFiles();
174: for (int i = 0; i < files.length; i++) {
175: deleteFile(files[i]);
176: }
177: }
178:
179: file.delete();
180: }
181:
182: /**
183: * Returns a file by combining <tt>path</tt> and <tt>fileName</tt> and using
184: * it as a path relative to the webapp.
185: */
186: public static File getFile(HttpServlet servlet, String path,
187: String fileName) {
188:
189: return new File(servlet.getServletContext().getRealPath(
190: path + File.separator + fileName));
191: }
192:
193: /**
194: * NOT UNIT TESTED Converts a list of <tt>FoldingTreeNode</tt>s (as created
195: * in {@link ResForm#reset ResForm.reset()}) to an array of filenames.
196: */
197: public static String[] nodesToFileNames(List nodes) {
198:
199: String[] fileNames = new String[nodes.size()];
200: int i = 0;
201: for (Iterator iter = nodes.iterator(); iter.hasNext();)
202: fileNames[i++] = ((FoldingTreeNode) iter.next()).getLabel();
203:
204: return fileNames;
205: }
206:
207: /**
208: * NOT UNIT TESTED Returns the file manager in the request, creating and
209: * initializing one if necessary.
210: */
211: public static FileManager getFileManager(ResPolicy policy,
212: HttpServletRequest request) {
213:
214: FileManager fileManager = (FileManager) request
215: .getAttribute(ResGlobals.FILEMANAGER_KEY);
216:
217: if (fileManager == null) {
218: fileManager = policy.newFileManager();
219: policy.initFileManager(request, fileManager);
220: request.setAttribute(ResGlobals.FILEMANAGER_KEY,
221: fileManager);
222: }
223:
224: return fileManager;
225: }
226:
227: /**
228: * NOT UNIT TESTED Instantiates the <tt>ResPolicy</tt> for defined in
229: * <tt>mapping</tt>'s parameter.
230: */
231: public static ResPolicy getPolicy(ActionMapping mapping) {
232:
233: try {
234: return (ResPolicy) Class.forName(mapping.getParameter())
235: .newInstance();
236: } catch (Exception e) {
237: throw new ResException(
238: "Unexpected exception while instantiating \""
239: + mapping.getParameter() + "\":"
240: + e.toString());
241: }
242: }
243:
244: /**
245: * Returns the file tree from the session, creating and initializing it if
246: * necessary.
247: */
248: public static FileTree getFileTree(ResPolicy policy,
249: HttpServletRequest request) {
250:
251: FileTree fileTree = (FileTree) request.getSession()
252: .getAttribute(ResGlobals.FILETREE_KEY);
253:
254: if (fileTree == null) {
255: fileTree = policy.newFileTree();
256: fileTree.build(getFileManager(policy, request));
257:
258: request.getSession().setAttribute(ResGlobals.FILETREE_KEY,
259: fileTree);
260: }
261:
262: return fileTree;
263: }
264:
265: // properties ///////////////////////////////////////////////////////////////
266:
267: // attributes ///////////////////////////////////////////////////////////////
268: }
|