View Javadoc

1   /*
2    * FCKeditor - The text editor for Internet - http://www.fckeditor.net
3    * Copyright (C) 2003-2008 Frederico Caldeira Knabben
4    * 
5    * == BEGIN LICENSE ==
6    * 
7    * Licensed under the terms of any of the following licenses at your
8    * choice:
9    * 
10   *  - GNU General Public License Version 2 or later (the "GPL")
11   *    http://www.gnu.org/licenses/gpl.html
12   * 
13   *  - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
14   *    http://www.gnu.org/licenses/lgpl.html
15   * 
16   *  - Mozilla Public License Version 1.1 or later (the "MPL")
17   *    http://www.mozilla.org/MPL/MPL-1.1.html
18   * 
19   * == END LICENSE ==
20   */
21  package net.fckeditor.connector;
22  
23  import java.io.File;
24  import java.io.IOException;
25  import java.io.PrintWriter;
26  import java.util.List;
27  
28  import javax.servlet.ServletException;
29  import javax.servlet.http.HttpServlet;
30  import javax.servlet.http.HttpServletRequest;
31  import javax.servlet.http.HttpServletResponse;
32  
33  import net.fckeditor.handlers.CommandHandler;
34  import net.fckeditor.handlers.ConnectorHandler;
35  import net.fckeditor.handlers.ExtensionsHandler;
36  import net.fckeditor.handlers.RequestCycleHandler;
37  import net.fckeditor.handlers.ResourceTypeHandler;
38  import net.fckeditor.response.UploadResponse;
39  import net.fckeditor.response.XmlResponse;
40  import net.fckeditor.tool.Utils;
41  import net.fckeditor.tool.UtilsFile;
42  import net.fckeditor.tool.UtilsResponse;
43  
44  import org.apache.commons.fileupload.FileItem;
45  import org.apache.commons.fileupload.FileItemFactory;
46  import org.apache.commons.fileupload.disk.DiskFileItemFactory;
47  import org.apache.commons.fileupload.servlet.ServletFileUpload;
48  import org.apache.commons.io.FilenameUtils;
49  import org.slf4j.Logger;
50  import org.slf4j.LoggerFactory;
51  
52  /**
53   * Servlet to upload and browse files.<br>
54   * 
55   * This servlet accepts 4 commands used to retrieve and create files and folders from a server
56   * directory. The allowed commands are:
57   * <ul>
58   * <li>GetFolders: Retrieve the list of directory under the current folder
59   * <li>GetFoldersAndFiles: Retrive the list of files and directory under the current folder
60   * <li>CreateFolder: Create a new directory under the current folder
61   * <li>FileUpload: Send a new file to the server (must be sent with a POST)
62   * </ul>
63   * 
64   * @version $Id: ConnectorServlet.java 1965 2008-05-08 10:37:51Z th-schwarz $
65   */
66  public class ConnectorServlet extends HttpServlet {
67  
68  	private static final long serialVersionUID = -5742008970929377161L;
69  	private static final Logger logger = LoggerFactory.getLogger(ConnectorServlet.class);
70  
71  	/**
72  	 * Initialize the servlet.<br>
73  	 * The default directory for user files will be constructed.
74  	 */
75  	public void init() throws ServletException, IllegalArgumentException {
76  		// check, if 'baseDir' exists
77  		String realDefaultUserFilesPath = getServletContext().getRealPath(
78  		        ConnectorHandler.getDefaultUserFilesPath());
79  
80  		File defaultUserFilesDir = new File(realDefaultUserFilesPath);
81  		UtilsFile.checkDirAndCreate(defaultUserFilesDir);
82  
83  		logger.info("ConnectorServlet successful initialized!");
84  	}
85  
86  	/**
87  	 * Manage the Get requests (GetFolders, GetFoldersAndFiles, CreateFolder).<br>
88  	 * 
89  	 * The servlet accepts commands sent in the following format:<br>
90  	 * connector?Command=CommandName&Type=ResourceType&CurrentFolder=FolderPath<br>
91  	 * <br>
92  	 * It executes the commands and then return the results to the client in XML format.
93  	 * 
94  	 */
95  	public void doGet(HttpServletRequest request, HttpServletResponse response)
96  	        throws ServletException, IOException {
97  		logger.debug("Entering ConnectorServlet#doGet");
98  
99  		response.setCharacterEncoding("UTF-8");
100 		response.setContentType("application/xml; charset=UTF-8");
101 		response.setHeader("Cache-Control", "no-cache");
102 		PrintWriter out = response.getWriter();
103 
104 		String commandStr = request.getParameter("Command");
105 		String typeStr = request.getParameter("Type");
106 		String currentFolderStr = request.getParameter("CurrentFolder");
107 
108 		logger.debug("Parameter Command: {}", commandStr);
109 		logger.debug("Parameter Type: {}", typeStr);
110 		logger.debug("Parameter CurrentFolder: {}", currentFolderStr);
111 
112 		XmlResponse xr;
113 
114 		if (!RequestCycleHandler.isEnabledForFileBrowsing(request))
115 			xr = new XmlResponse(XmlResponse.EN_ERROR, Messages.NOT_AUTHORIZED_FOR_BROWSING);
116 		else if (!CommandHandler.isValidForGet(commandStr))
117 			xr = new XmlResponse(XmlResponse.EN_ERROR, Messages.INVALID_COMMAND);
118 		else if (typeStr != null && !ResourceTypeHandler.isValid(typeStr))
119 			xr = new XmlResponse(XmlResponse.EN_ERROR, Messages.INVALID_TYPE);
120 		else if (!UtilsFile.isValidPath(currentFolderStr))
121 			xr = new XmlResponse(XmlResponse.EN_ERROR, Messages.INVALID_CURRENT_FOLDER);
122 		else {
123 			CommandHandler command = CommandHandler.getCommand(commandStr);
124 			ResourceTypeHandler resourceType = ResourceTypeHandler.getDefaultResourceType(typeStr);
125 
126 			String typePath = UtilsResponse.constructResponseUrl(request, resourceType,
127 			        currentFolderStr, false, false);
128 			String typeDirPath = getServletContext().getRealPath(typePath);
129 
130 			File typeDir = new File(typeDirPath);
131 			UtilsFile.checkDirAndCreate(typeDir);
132 
133 			File currentDir = new File(typeDir, currentFolderStr);
134 
135 			if (!currentDir.exists())
136 				xr = new XmlResponse(XmlResponse.EN_INVALID_FOLDER_NAME);
137 			else {
138 
139 				xr = new XmlResponse(command, resourceType, currentFolderStr, UtilsResponse
140 				        .constructResponseUrl(request, resourceType, currentFolderStr, true,
141 				                ConnectorHandler.isFullUrl()));
142 
143 				if (command.equals(CommandHandler.GET_FOLDERS))
144 					xr.setFolders(currentDir);
145 				else if (command.equals(CommandHandler.GET_FOLDERS_AND_FILES))
146 					xr.setFoldersAndFiles(currentDir);
147 				else if (command.equals(CommandHandler.CREATE_FOLDER)) {
148 					String newFolderStr = UtilsFile.sanitizeFolderName(request
149 					        .getParameter("NewFolderName"));
150 					logger.debug("Parameter NewFolderName: {}", newFolderStr);
151 
152 					File newFolder = new File(currentDir, newFolderStr);
153 					int errorNumber = XmlResponse.EN_UKNOWN;
154 
155 					if (newFolder.exists())
156 						errorNumber = XmlResponse.EN_ALREADY_EXISTS;
157 					else {
158 						try {
159 							errorNumber = (newFolder.mkdir()) ? XmlResponse.EN_OK
160 							        : XmlResponse.EN_INVALID_FOLDER_NAME;
161 						} catch (SecurityException e) {
162 							errorNumber = XmlResponse.EN_SECURITY_ERROR;
163 						}
164 					}
165 					xr.setError(errorNumber);
166 				}
167 			}
168 		}
169 
170 		out.print(xr);
171 		out.flush();
172 		out.close();
173 		logger.debug("Exiting ConnectorServlet#doGet");
174 	}
175 
176 	/**
177 	 * Manage the Post requests (FileUpload).<br>
178 	 * 
179 	 * The servlet accepts commands sent in the following format:<br>
180 	 * connector?Command=FileUpload&Type=ResourceType&CurrentFolder=FolderPath<br>
181 	 * <br>
182 	 * It store the file (renaming it in case a file with the same name exists) and then return an
183 	 * HTML file with a javascript command in it.
184 	 */
185 	@SuppressWarnings("unchecked")
186 	public void doPost(HttpServletRequest request, HttpServletResponse response)
187 	        throws ServletException, IOException {
188 		logger.debug("Entering Connector#doPost");
189 
190 		response.setCharacterEncoding("UTF-8");
191 		response.setContentType("text/html; charset=UTF-8");
192 		response.setHeader("Cache-Control", "no-cache");
193 		PrintWriter out = response.getWriter();
194 
195 		String commandStr = request.getParameter("Command");
196 		String typeStr = request.getParameter("Type");
197 		String currentFolderStr = request.getParameter("CurrentFolder");
198 
199 		logger.debug("Parameter Command: {}", commandStr);
200 		logger.debug("Parameter Type: {}", typeStr);
201 		logger.debug("Parameter CurrentFolder: {}", currentFolderStr);
202 
203 		UploadResponse ur;
204 
205 		// if this is a QuickUpload-Request, 'commandStr' and 'currentFolderStr' are empty
206 		if (Utils.isEmpty(commandStr) && Utils.isEmpty(currentFolderStr)) {
207 			commandStr = "QuickUpload";
208 			currentFolderStr = "/";
209 		}
210 
211 		if (!RequestCycleHandler.isEnabledForFileUpload(request))
212 			ur = new UploadResponse(UploadResponse.EN_SECURITY_ERROR, null, null,
213 			        Messages.NOT_AUTHORIZED_FOR_UPLOAD);
214 		else if (!CommandHandler.isValidForPost(commandStr))
215 			ur = new UploadResponse(UploadResponse.EN_ERROR, null, null, Messages.INVALID_COMMAND);
216 		else if (typeStr != null && !ResourceTypeHandler.isValid(typeStr))
217 			ur = new UploadResponse(UploadResponse.EN_ERROR, null, null, Messages.INVALID_TYPE);
218 		else if (!UtilsFile.isValidPath(currentFolderStr))
219 			ur = UploadResponse.UR_INVALID_CURRENT_FOLDER;
220 		else {
221 			ResourceTypeHandler resourceType = ResourceTypeHandler.getDefaultResourceType(typeStr);
222 
223 			String typePath = UtilsResponse.constructResponseUrl(request, resourceType,
224 			        currentFolderStr, false, false);
225 			String typeDirPath = getServletContext().getRealPath(typePath);
226 
227 			File typeDir = new File(typeDirPath);
228 			UtilsFile.checkDirAndCreate(typeDir);
229 
230 			File currentDir = new File(typeDir, currentFolderStr);
231 
232 			if (!currentDir.exists())
233 				ur = UploadResponse.UR_INVALID_CURRENT_FOLDER;
234 			else {
235 
236 				String newFilename = null;
237 				FileItemFactory factory = new DiskFileItemFactory();
238 				ServletFileUpload upload = new ServletFileUpload(factory);
239 
240 				try {
241 
242 					List<FileItem> items = upload.parseRequest(request);
243 
244 					// We upload only one file at the same time
245 					FileItem uplFile = items.get(0);
246 					String rawName = UtilsFile.sanitizeFileName(uplFile.getName());
247 					String filename = FilenameUtils.getName(rawName);
248 					String baseName = FilenameUtils.removeExtension(filename);
249 					String extension = FilenameUtils.getExtension(filename);
250 
251 					if (!ExtensionsHandler.isAllowed(resourceType, extension))
252 						ur = new UploadResponse(UploadResponse.EN_INVALID_EXTENSION);
253 					else {
254 
255 						// construct an unique file name
256 						File pathToSave = new File(currentDir, filename);
257 						int counter = 1;
258 						while (pathToSave.exists()) {
259 							newFilename = baseName.concat("(").concat(String.valueOf(counter))
260 							        .concat(")").concat(".").concat(extension);
261 							pathToSave = new File(currentDir, newFilename);
262 							counter++;
263 						}
264 
265 						if (Utils.isEmpty(newFilename))
266 							ur = new UploadResponse(UploadResponse.EN_OK, UtilsResponse
267 							        .constructResponseUrl(request, resourceType, currentFolderStr,
268 							                true, ConnectorHandler.isFullUrl()).concat(filename));
269 						else
270 							ur = new UploadResponse(UploadResponse.EN_RENAMED,
271 							        UtilsResponse.constructResponseUrl(request, resourceType,
272 							                currentFolderStr, true, ConnectorHandler.isFullUrl())
273 							                .concat(newFilename), newFilename);
274 
275 						// secure image check
276 						if (resourceType.equals(ResourceTypeHandler.IMAGE)
277 						        && ConnectorHandler.isSecureImageUploads()) {
278 							if (UtilsFile.isImage(uplFile.getInputStream()))
279 								uplFile.write(pathToSave);
280 							else {
281 								uplFile.delete();
282 								ur = new UploadResponse(UploadResponse.EN_INVALID_EXTENSION);
283 							}
284 						} else
285 							uplFile.write(pathToSave);
286 
287 					}
288 				} catch (Exception e) {
289 					ur = new UploadResponse(UploadResponse.EN_SECURITY_ERROR);
290 				}
291 			}
292 
293 		}
294 
295 		out.print(ur);
296 		out.flush();
297 		out.close();
298 
299 		logger.debug("Exiting Connector#doPost");
300 	}
301 
302 }