/*
* BitstreamServlet.java
*
* Version: $Revision: 4430 $
*
* Date: $Date: 2009-10-10 17:21:30 +0000 (Sat, 10 Oct 2009) $
*
* Copyright (c) 2002-2005, Hewlett-Packard Company and Massachusetts
* Institute of Technology. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of the Hewlett-Packard Company nor the name of the
* Massachusetts Institute of Technology nor the names of their
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*/
package org.dspace.app.webui.servlet;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.sql.SQLException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.mail.internet.MimeUtility;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.log4j.Logger;
import org.dspace.app.webui.util.JSPManager;
import org.dspace.authorize.AuthorizeException;
import org.dspace.content.Bitstream;
import org.dspace.content.Bundle;
import org.dspace.content.DSpaceObject;
import org.dspace.content.Item;
import org.dspace.core.ConfigurationManager;
import org.dspace.core.Constants;
import org.dspace.core.Context;
import org.dspace.core.LogManager;
import org.dspace.core.Utils;
import org.dspace.handle.HandleManager;
import org.dspace.services.model.Event;
import org.dspace.usage.UsageEvent;
import org.dspace.utils.DSpace;
/**
* Servlet for retrieving bitstreams. The bits are simply piped to the user. If
* there is an <code>If-Modified-Since</code> header, only a 304 status code
* is returned if the containing item has not been modified since that date.
* <P>
* <code>/bitstream/handle/sequence_id/filename</code>
*
* @author Robert Tansley
* @version $Revision: 4430 $
*/
public class BitstreamServlet extends DSpaceServlet
{
/** log4j category */
private static Logger log = Logger.getLogger(BitstreamServlet.class);
/**
* Threshold on Bitstream size before content-disposition will be set.
*/
private int threshold;
/**
* Pattern used to get file.ext from filename (which can be a path)
*/
private static Pattern p = Pattern.compile("[^/]*$");
@Override
public void init(ServletConfig arg0) throws ServletException {
super.init(arg0);
threshold = ConfigurationManager
.getIntProperty("webui.content_disposition_threshold");
}
@Override
protected void doDSGet(Context context, HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException,
SQLException, AuthorizeException
{
Item item = null;
Bitstream bitstream = null;
// Get the ID from the URL
String idString = request.getPathInfo();
String handle = "";
String sequenceText = "";
String filename = null;
int sequenceID;
if (idString == null)
{
idString = "";
}
// Parse 'handle' and 'sequence' (bitstream seq. number) out
// of remaining URL path, which is typically of the format:
// {handle}/{sequence}/{bitstream-name}
// But since the bitstream name MAY have any number of "/"s in
// it, and the handle is guaranteed to have one slash, we
// scan from the start to pick out handle and sequence:
// Remove leading slash if any:
if (idString.startsWith("/"))
{
idString = idString.substring(1);
}
// skip first slash within handle
int slashIndex = idString.indexOf('/');
if (slashIndex != -1)
{
slashIndex = idString.indexOf('/', slashIndex + 1);
if (slashIndex != -1)
{
handle = idString.substring(0, slashIndex);
int slash2 = idString.indexOf('/', slashIndex + 1);
if (slash2 != -1)
{
sequenceText = idString.substring(slashIndex+1,slash2);
filename = idString.substring(slash2+1);
}
}
}
try
{
sequenceID = Integer.parseInt(sequenceText);
}
catch (NumberFormatException nfe)
{
sequenceID = -1;
}
// Now try and retrieve the item
DSpaceObject dso = HandleManager.resolveToObject(context, handle);
// Make sure we have valid item and sequence number
if (dso != null && dso.getType() == Constants.ITEM && sequenceID >= 0)
{
item = (Item) dso;
if (item.isWithdrawn())
{
log.info(LogManager.getHeader(context, "view_bitstream",
"handle=" + handle + ",withdrawn=true"));
JSPManager.showJSP(request, response, "/tombstone.jsp");
return;
}
boolean found = false;
Bundle[] bundles = item.getBundles();
for (int i = 0; (i < bundles.length) && !found; i++)
{
Bitstream[] bitstreams = bundles[i].getBitstreams();
for (int k = 0; (k < bitstreams.length) && !found; k++)
{
if (sequenceID == bitstreams[k].getSequenceID())
{
bitstream = bitstreams[k];
found = true;
}
}
}
}
if (bitstream == null || filename == null
|| !filename.equals(bitstream.getName()))
{
// No bitstream found or filename was wrong -- ID invalid
log.info(LogManager.getHeader(context, "invalid_id", "path="
+ idString));
JSPManager.showInvalidIDError(request, response, idString,
Constants.BITSTREAM);
return;
}
log.info(LogManager.getHeader(context, "view_bitstream",
"bitstream_id=" + bitstream.getID()));
//new UsageEvent().fire(request, context, AbstractUsageEvent.VIEW,
// Constants.BITSTREAM, bitstream.getID());
new DSpace().getEventService().fireEvent(
new UsageEvent(
UsageEvent.Action.VIEW,
request,
context,
bitstream));
// Modification date
// Only use last-modified if this is an anonymous access
// - caching content that may be generated under authorisation
// is a security problem
if (context.getCurrentUser() == null)
{
// TODO: Currently the date of the item, since we don't have dates
// for files
response.setDateHeader("Last-Modified", item.getLastModified()
.getTime());
// Check for if-modified-since header
long modSince = request.getDateHeader("If-Modified-Since");
if (modSince != -1 && item.getLastModified().getTime() < modSince)
{
// Item has not been modified since requested date,
// hence bitstream has not; return 304
response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
return;
}
}
// Pipe the bits
InputStream is = bitstream.retrieve();
// Set the response MIME type
response.setContentType(bitstream.getFormat().getMIMEType());
// Response length
response.setHeader("Content-Length", String
.valueOf(bitstream.getSize()));
if(threshold != -1 && bitstream.getSize() >= threshold)
{
setBitstreamDisposition(bitstream.getName(), request, response);
}
Utils.bufferedCopy(is, response.getOutputStream());
is.close();
response.getOutputStream().flush();
}
/**
* Evaluate filename and client and encode appropriate disposition
*
* @param uri
* @param request
* @param response
* @throws UnsupportedEncodingException
*/
private void setBitstreamDisposition(String filename, HttpServletRequest request,
HttpServletResponse response)
{
String name = filename;
Matcher m = p.matcher(name);
if (m.find() && !m.group().equals(""))
{
name = m.group();
}
try
{
String agent = request.getHeader("USER-AGENT");
if (null != agent && -1 != agent.indexOf("MSIE"))
{
name = URLEncoder.encode(name, "UTF8");
}
else if (null != agent && -1 != agent.indexOf("Mozilla"))
{
name = MimeUtility.encodeText(name, "UTF8", "B");
}
}
catch (UnsupportedEncodingException e)
{
log.error(e.getMessage(),e);
}
finally
{
response.setHeader("Content-Disposition", "attachment;filename=" + name);
}
}
}