/**********************************************************************************
* $URL: https://source.sakaiproject.org/svn/access/trunk/access-impl/impl/src/java/org/sakaiproject/access/tool/WebServlet.java $
* $Id: WebServlet.java 105078 2012-02-24 23:00:38Z ottenhoff@longsight.com $
***********************************************************************************
*
* Copyright (c) 2006, 2007, 2008 The Sakai Foundation
*
* Licensed under the Educational Community License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.opensource.org/licenses/ECL-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
**********************************************************************************/
package org.sakaiproject.access.tool;
import java.io.IOException;
import java.util.Enumeration;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUpload;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.sakaiproject.component.cover.ComponentManager;
import org.sakaiproject.content.api.ContentCollection;
import org.sakaiproject.content.api.ContentHostingService;
import org.sakaiproject.content.api.ContentResource;
import org.sakaiproject.content.api.ContentResourceEdit;
import org.sakaiproject.entity.api.Entity;
import org.sakaiproject.entity.api.Reference;
import org.sakaiproject.entity.api.ResourceProperties;
import org.sakaiproject.entity.api.ResourcePropertiesEdit;
import org.sakaiproject.entity.cover.EntityManager;
import org.sakaiproject.event.cover.NotificationService;
import org.sakaiproject.exception.IdUnusedException;
import org.sakaiproject.exception.IdUsedException;
import org.sakaiproject.exception.InconsistentException;
import org.sakaiproject.time.api.TimeBreakdown;
import org.sakaiproject.time.cover.TimeService;
import org.sakaiproject.user.api.User;
import org.sakaiproject.user.cover.UserDirectoryService;
import org.sakaiproject.tool.api.Session;
import org.sakaiproject.tool.cover.SessionManager;
/**
* <p>
* Web extends access: all references are assumed to be under "/content", and POST to add a file is supported.
* </p>
*
* @author Sakai Software Development Team
*/
public class WebServlet extends AccessServlet
{
/** Our log (commons). */
private static Log M_log = LogFactory.getLog(WebServlet.class);
private ContentHostingService contentHostingService;
/* (non-Javadoc)
* @see org.sakaiproject.access.tool.AccessServlet#init(javax.servlet.ServletConfig)
*/
@Override
public void init(ServletConfig config) throws ServletException
{
super.init(config);
contentHostingService = (ContentHostingService) ComponentManager.get(ContentHostingService.class.getName());
}
/**
* Set active session according to sessionId parameter
*/
private void setSession( HttpServletRequest req )
{
String sessionId = req.getParameter("session");
if ( sessionId != null)
{
Session session = SessionManager.getSession(sessionId);
if (session != null)
{
session.setActive();
SessionManager.setCurrentSession(session);
}
}
}
/**
* respond to an HTTP GET request
*
* @param req
* HttpServletRequest object with the client request
* @param res
* HttpServletResponse object back to the client
* @exception ServletException
* in case of difficulties
* @exception IOException
* in case of difficulties
*/
public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException
{
setSession(req);
super.dispatch(req, res);
}
/**
* respond to an HTTP POST request; only to handle the login process
*
* @param req
* HttpServletRequest object with the client request
* @param res
* HttpServletResponse object back to the client
* @exception ServletException
* in case of difficulties
* @exception IOException
* in case of difficulties
*/
public void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException
{
// catch the login helper posts
String option = req.getPathInfo();
String[] parts = option.split("/");
if ((parts.length == 2) && ((parts[1].equals("login"))))
{
doLogin(req, res, null);
}
else if (FileUpload.isMultipartContent(req))
{
setSession(req);
postUpload(req, res);
}
else
{
sendError(res, HttpServletResponse.SC_NOT_FOUND);
}
}
/**
* Make any changes needed to the path before final "ref" processing.
*
* @param path
* The path from the request.
* @param req
* The request object.
* @return The path to use to make the Reference for further processing.
*/
protected String preProcessPath(String path, HttpServletRequest req)
{
// everything we work with is down the "content" part of the Sakai access URL space
// if path is just "/", we don't really know if the request was to .../SERVLET or .../SERVLET/ - we want to preserve the trailing slash
// the request URI will tell us
if ("/".equals(path) && !(req.getRequestURI().endsWith("/"))) return "/content";
return "/content" + path;
}
/**
* Handle file upload requests.
*
* @param req
* @param res
*/
protected void postUpload(HttpServletRequest req, HttpServletResponse res)
{
String path = req.getPathInfo();
// System.out.println("path " + path);
if (path == null) path = "";
// assume caller has verified that it is a request for content and that it's multipart
// loop over attributes in request, picking out the ones
// that are file uploads and doing them
for (Enumeration e = req.getAttributeNames(); e.hasMoreElements();)
{
String iname = (String) e.nextElement();
// System.out.println("Item " + iname);
Object o = req.getAttribute(iname);
// NOTE: Fileitem is from
// org.apache.commons.fileupload.FileItem, not
// sakai's parameterparser version
if (o != null && o instanceof FileItem)
{
FileItem fi = (FileItem) o;
// System.out.println("found file " + fi.getName());
if (!writeFile(fi.getName(), fi.getContentType(), fi.get(), path, req, res, true)) return;
}
}
}
protected boolean writeFile(String name, String type, byte[] data, String dir, HttpServletRequest req,
HttpServletResponse resp, boolean mkdir)
{
try
{
// validate filename. Need to be fairly careful.
int i = name.lastIndexOf(Entity.SEPARATOR);
if (i >= 0) name = name.substring(i + 1);
if (name.length() < 1)
{
// System.out.println("no name left / removal");
resp.sendError(HttpServletResponse.SC_FORBIDDEN);
return false;
}
// do our web thing with the path
dir = preProcessPath(dir, req);
// make sure there's a trailing separator
if (!dir.endsWith(Entity.SEPARATOR)) dir = dir + Entity.SEPARATOR;
// get a reference to the content collection - this lets us use alias and short refs
Reference ref = EntityManager.newReference(dir);
// the reference id replaces the dir - as a fully qualified path (no alias, no short ref)
dir = ref.getId();
String path = dir + name;
ResourcePropertiesEdit resourceProperties = contentHostingService.newResourceProperties();
// Try to delete the resource
try
{
// System.out.println("Trying Del " + path);
// The existing document may be a collection or a file.
boolean isCollection = contentHostingService.getProperties(path).getBooleanProperty(
ResourceProperties.PROP_IS_COLLECTION);
if (isCollection)
{
// System.out.println("Can't del, iscoll");
resp.sendError(HttpServletResponse.SC_FORBIDDEN);
return false;
}
else
{
// not sure why removeesource(path) didn't
// work for my workspace
ContentResourceEdit edit = contentHostingService.editResource(path);
// if (edit != null)
// System.out.println("Got edit");
contentHostingService.removeResource(edit);
}
}
catch (IdUnusedException e)
{
// Normal situation - nothing to do
}
catch (Exception e)
{
// System.out.println("Can't del, exception " + e.getClass() + ": " + e.getMessage());
resp.sendError(HttpServletResponse.SC_FORBIDDEN);
return false;
}
// Add the resource
try
{
User user = UserDirectoryService.getCurrentUser();
TimeBreakdown timeBreakdown = TimeService.newTime().breakdownLocal();
String mycopyright = "copyright (c)" + " " + timeBreakdown.getYear() + ", " + user.getDisplayName()
+ ". All Rights Reserved. ";
resourceProperties.addProperty(ResourceProperties.PROP_COPYRIGHT, mycopyright);
resourceProperties.addProperty(ResourceProperties.PROP_DISPLAY_NAME, name);
// System.out.println("Trying Add " + path);
ContentResource resource = contentHostingService.addResource(path, type, data, resourceProperties,
NotificationService.NOTI_NONE);
}
catch (InconsistentException e)
{
// get this error if containing dir doesn't exist
if (mkdir)
{
try
{
ContentCollection collection = contentHostingService.addCollection(dir, resourceProperties);
return writeFile(name, type, data, dir, req, resp, false);
}
catch (Throwable ee)
{
}
}
// System.out.println("Add fail, inconsistent");
resp.sendError(HttpServletResponse.SC_CONFLICT);
return false;
}
catch (IdUsedException e)
{
// Should not happen because we deleted above (unless tawo requests at same time)
// System.out.println("Add fail, in use");
M_log.warn("access post IdUsedException:" + e.getMessage());
resp.sendError(HttpServletResponse.SC_CONFLICT);
return false;
}
catch (Exception e)
{
// System.out.println("Add failed, exception " + e.getClass() + ": " + e.getMessage());
resp.sendError(HttpServletResponse.SC_FORBIDDEN);
return false;
}
}
catch (IOException e)
{
// System.out.println("overall fail IOException " + e);
}
return true;
}
}