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 which interact with the server-side
56   * filesystem.<br />
57   * The allowed commands are:
58   * <ul>
59   * <li><code>GetFolders</code>: Retrieves a list of folders in the current
60   * folder</li>
61   * <li><code>GetFoldersAndFiles</code>: Retrives a list of files and folders
62   * in the current folder</li>
63   * <li><code>CreateFolder</code>: Creates a new folder in the current folder</li>
64   * <li><code>FileUpload</code>: Stores an uploaded file into the current
65   * folder. (must be sent with POST)</li>
66   * </ul>
67   * 
68   * @version $Id: ConnectorServlet.java 2101 2008-06-22 22:00:48Z mosipov $
69   */
70  public class ConnectorServlet extends HttpServlet {
71  
72  	private static final long serialVersionUID = -5742008970929377161L;
73  	private static final Logger logger = LoggerFactory.getLogger(ConnectorServlet.class);
74  
75  	/**
76  	 * Initialize the servlet: <code>mkdir</code> &lt;DefaultUserFilesPath&gt;
77  	 */
78  	public void init() throws ServletException, IllegalArgumentException {
79  		String realDefaultUserFilesPath = getServletContext().getRealPath(
80  		        ConnectorHandler.getDefaultUserFilesPath());
81  
82  		File defaultUserFilesDir = new File(realDefaultUserFilesPath);
83  		UtilsFile.checkDirAndCreate(defaultUserFilesDir);
84  
85  		logger.info("ConnectorServlet successfully initialized!");
86  	}
87  
88  	/**
89  	 * Manage the <code>GET</code> requests (<code>GetFolders</code>,
90  	 * <code>GetFoldersAndFiles</code>, <code>CreateFolder</code>).<br/>
91  	 * 
92  	 * The servlet accepts commands sent in the following format:<br/>
93  	 * <code>connector?Command=&lt;CommandName&gt;&Type=&lt;ResourceType&gt;&CurrentFolder=&lt;FolderPath&gt;</code>
94  	 * <p>
95  	 * It executes the commands and then returns the result to the client in XML
96  	 * format.
97  	 * </p>
98  	 */
99  	public void doGet(HttpServletRequest request, HttpServletResponse response)
100 	        throws ServletException, IOException {
101 		logger.debug("Entering ConnectorServlet#doGet");
102 
103 		response.setCharacterEncoding("UTF-8");
104 		response.setContentType("application/xml; charset=UTF-8");
105 		response.setHeader("Cache-Control", "no-cache");
106 		PrintWriter out = response.getWriter();
107 
108 		String commandStr = request.getParameter("Command");
109 		String typeStr = request.getParameter("Type");
110 		String currentFolderStr = request.getParameter("CurrentFolder");
111 
112 		logger.debug("Parameter Command: {}", commandStr);
113 		logger.debug("Parameter Type: {}", typeStr);
114 		logger.debug("Parameter CurrentFolder: {}", currentFolderStr);
115 
116 		XmlResponse xr;
117 
118 		if (!RequestCycleHandler.isEnabledForFileBrowsing(request))
119 			xr = new XmlResponse(XmlResponse.EN_ERROR, Messages.NOT_AUTHORIZED_FOR_BROWSING);
120 		else if (!CommandHandler.isValidForGet(commandStr))
121 			xr = new XmlResponse(XmlResponse.EN_ERROR, Messages.INVALID_COMMAND);
122 		else if (typeStr != null && !ResourceTypeHandler.isValid(typeStr))
123 			xr = new XmlResponse(XmlResponse.EN_ERROR, Messages.INVALID_TYPE);
124 		else if (!UtilsFile.isValidPath(currentFolderStr))
125 			xr = new XmlResponse(XmlResponse.EN_ERROR, Messages.INVALID_CURRENT_FOLDER);
126 		else {
127 			CommandHandler command = CommandHandler.getCommand(commandStr);
128 			ResourceTypeHandler resourceType = ResourceTypeHandler.getDefaultResourceType(typeStr);
129 
130 			String typePath = UtilsFile.constructServerSidePath(request, resourceType);
131 			String typeDirPath = getServletContext().getRealPath(typePath);
132 
133 			File typeDir = new File(typeDirPath);
134 			UtilsFile.checkDirAndCreate(typeDir);
135 
136 			File currentDir = new File(typeDir, currentFolderStr);
137 
138 			if (!currentDir.exists())
139 				xr = new XmlResponse(XmlResponse.EN_INVALID_FOLDER_NAME);
140 			else {
141 
142 				xr = new XmlResponse(command, resourceType, currentFolderStr, UtilsResponse
143 				        .constructResponseUrl(request, resourceType, currentFolderStr, true,
144 				                ConnectorHandler.isFullUrl()));
145 
146 				if (command.equals(CommandHandler.GET_FOLDERS))
147 					xr.setFolders(currentDir);
148 				else if (command.equals(CommandHandler.GET_FOLDERS_AND_FILES))
149 					xr.setFoldersAndFiles(currentDir);
150 				else if (command.equals(CommandHandler.CREATE_FOLDER)) {
151 					String newFolderStr = UtilsFile.sanitizeFolderName(request
152 					        .getParameter("NewFolderName"));
153 					logger.debug("Parameter NewFolderName: {}", newFolderStr);
154 
155 					File newFolder = new File(currentDir, newFolderStr);
156 					int errorNumber = XmlResponse.EN_UKNOWN;
157 
158 					if (newFolder.exists())
159 						errorNumber = XmlResponse.EN_ALREADY_EXISTS;
160 					else {
161 						try {
162 							errorNumber = (newFolder.mkdir()) ? XmlResponse.EN_OK
163 							        : XmlResponse.EN_INVALID_FOLDER_NAME;
164 						} catch (SecurityException e) {
165 							errorNumber = XmlResponse.EN_SECURITY_ERROR;
166 						}
167 					}
168 					xr.setError(errorNumber);
169 				}
170 			}
171 		}
172 
173 		out.print(xr);
174 		out.flush();
175 		out.close();
176 		logger.debug("Exiting ConnectorServlet#doGet");
177 	}
178 
179 	/**
180 	 * Manage the <code>POST</code> requests (<code>FileUpload</code>).<br />
181 	 * 
182 	 * The servlet accepts commands sent in the following format:<br />
183 	 * <code>connector?Command=&lt;FileUpload&gt;&Type=&lt;ResourceType&gt;&CurrentFolder=&lt;FolderPath&gt;</code>
184 	 * with the file in the <code>POST</code> body.<br />
185 	 * <br>
186 	 * It stores an uploaded file (renames a file if another exists with the
187 	 * same name) and then returns the JavaScript callback.
188 	 */
189 	@SuppressWarnings("unchecked")
190 	public void doPost(HttpServletRequest request, HttpServletResponse response)
191 	        throws ServletException, IOException {
192 		logger.debug("Entering Connector#doPost");
193 
194 		response.setCharacterEncoding("UTF-8");
195 		response.setContentType("text/html; charset=UTF-8");
196 		response.setHeader("Cache-Control", "no-cache");
197 		PrintWriter out = response.getWriter();
198 
199 		String commandStr = request.getParameter("Command");
200 		String typeStr = request.getParameter("Type");
201 		String currentFolderStr = request.getParameter("CurrentFolder");
202 
203 		logger.debug("Parameter Command: {}", commandStr);
204 		logger.debug("Parameter Type: {}", typeStr);
205 		logger.debug("Parameter CurrentFolder: {}", currentFolderStr);
206 
207 		UploadResponse ur;
208 
209 		// if this is a QuickUpload request, 'commandStr' and 'currentFolderStr'
210 		// are empty
211 		if (Utils.isEmpty(commandStr) && Utils.isEmpty(currentFolderStr)) {
212 			commandStr = "QuickUpload";
213 			currentFolderStr = "/";
214 		}
215 
216 		if (!RequestCycleHandler.isEnabledForFileUpload(request))
217 			ur = new UploadResponse(UploadResponse.SC_SECURITY_ERROR, null, null,
218 			        Messages.NOT_AUTHORIZED_FOR_UPLOAD);
219 		else if (!CommandHandler.isValidForPost(commandStr))
220 			ur = new UploadResponse(UploadResponse.SC_ERROR, null, null, Messages.INVALID_COMMAND);
221 		else if (typeStr != null && !ResourceTypeHandler.isValid(typeStr))
222 			ur = new UploadResponse(UploadResponse.SC_ERROR, null, null, Messages.INVALID_TYPE);
223 		else if (!UtilsFile.isValidPath(currentFolderStr))
224 			ur = UploadResponse.UR_INVALID_CURRENT_FOLDER;
225 		else {
226 			ResourceTypeHandler resourceType = ResourceTypeHandler.getDefaultResourceType(typeStr);
227 
228 			String typePath = UtilsFile.constructServerSidePath(request, resourceType);
229 			String typeDirPath = getServletContext().getRealPath(typePath);
230 
231 			File typeDir = new File(typeDirPath);
232 			UtilsFile.checkDirAndCreate(typeDir);
233 
234 			File currentDir = new File(typeDir, currentFolderStr);
235 
236 			if (!currentDir.exists())
237 				ur = UploadResponse.UR_INVALID_CURRENT_FOLDER;
238 			else {
239 
240 				String newFilename = null;
241 				FileItemFactory factory = new DiskFileItemFactory();
242 				ServletFileUpload upload = new ServletFileUpload(factory);
243 
244 				try {
245 
246 					List<FileItem> items = upload.parseRequest(request);
247 
248 					// We upload only one file at the same time
249 					FileItem uplFile = items.get(0);
250 					String rawName = UtilsFile.sanitizeFileName(uplFile.getName());
251 					String filename = FilenameUtils.getName(rawName);
252 					String baseName = FilenameUtils.removeExtension(filename);
253 					String extension = FilenameUtils.getExtension(filename);
254 
255 					if (!ExtensionsHandler.isAllowed(resourceType, extension))
256 						ur = new UploadResponse(UploadResponse.SC_INVALID_EXTENSION);
257 					else {
258 
259 						// construct an unique file name
260 						File pathToSave = new File(currentDir, filename);
261 						int counter = 1;
262 						while (pathToSave.exists()) {
263 							newFilename = baseName.concat("(").concat(String.valueOf(counter))
264 							        .concat(")").concat(".").concat(extension);
265 							pathToSave = new File(currentDir, newFilename);
266 							counter++;
267 						}
268 
269 						if (Utils.isEmpty(newFilename))
270 							ur = new UploadResponse(UploadResponse.SC_OK, UtilsResponse
271 							        .constructResponseUrl(request, resourceType, currentFolderStr,
272 							                true, ConnectorHandler.isFullUrl()).concat(filename));
273 						else
274 							ur = new UploadResponse(UploadResponse.SC_RENAMED,
275 							        UtilsResponse.constructResponseUrl(request, resourceType,
276 							                currentFolderStr, true, ConnectorHandler.isFullUrl())
277 							                .concat(newFilename), newFilename);
278 
279 						// secure image check
280 						if (resourceType.equals(ResourceTypeHandler.IMAGE)
281 						        && ConnectorHandler.isSecureImageUploads()) {
282 							if (UtilsFile.isImage(uplFile.getInputStream()))
283 								uplFile.write(pathToSave);
284 							else {
285 								uplFile.delete();
286 								ur = new UploadResponse(UploadResponse.SC_INVALID_EXTENSION);
287 							}
288 						} else
289 							uplFile.write(pathToSave);
290 
291 					}
292 				} catch (Exception e) {
293 					ur = new UploadResponse(UploadResponse.SC_SECURITY_ERROR);
294 				}
295 			}
296 
297 		}
298 
299 		out.print(ur);
300 		out.flush();
301 		out.close();
302 
303 		logger.debug("Exiting Connector#doPost");
304 	}
305 
306 }