001: package org.purl.sword.server;
002:
003: import java.io.File;
004: import java.io.FileInputStream;
005: import java.io.FileOutputStream;
006: import java.io.IOException;
007: import java.io.InputStream;
008: import java.io.OutputStream;
009: import java.io.PrintWriter;
010: import java.security.NoSuchAlgorithmException;
011: import java.util.Date;
012: import java.util.StringTokenizer;
013:
014: import javax.servlet.ServletException;
015: import javax.servlet.http.HttpServlet;
016: import javax.servlet.http.HttpServletRequest;
017: import javax.servlet.http.HttpServletResponse;
018:
019: import org.apache.commons.codec.binary.Base64;
020: import org.apache.log4j.Logger;
021: import org.purl.sword.base.ChecksumUtils;
022: import org.purl.sword.base.Deposit;
023: import org.purl.sword.base.DepositResponse;
024: import org.purl.sword.base.HttpHeaders;
025: import org.purl.sword.base.SWORDAuthenticationException;
026: import org.purl.sword.base.SWORDContentTypeException;
027: import org.purl.sword.base.SWORDException;
028:
029: public class DepositServlet extends HttpServlet {
030:
031: private SWORDServer myRepository;
032:
033: private String authN;
034:
035: private String tempDirectory;
036:
037: private static int counter = 0;
038:
039: private static Logger log = Logger.getLogger(DepositServlet.class);
040:
041: public void init() {
042: // Instantiate the correct SWORD Server class
043: String className = getServletContext().getInitParameter(
044: "server-class");
045: if (className == null) {
046: log
047: .fatal("Unable to read value of 'sword-server-class' from Servlet context");
048: } else {
049: try {
050: myRepository = (SWORDServer) Class.forName(className)
051: .newInstance();
052: log.info("Using " + className + " as the SWORDServer");
053: } catch (Exception e) {
054: log
055: .fatal("Unable to instantiate class from 'sword-server-class': "
056: + className);
057: }
058: }
059:
060: authN = getServletContext().getInitParameter(
061: "authentication-method");
062: if ((authN == null) || (authN.equals(""))) {
063: authN = "None";
064: }
065: log.info("Authentication type set to: " + authN);
066:
067: tempDirectory = getServletContext().getInitParameter(
068: "upload-temp-directory");
069: if ((tempDirectory == null) || (tempDirectory.equals(""))) {
070: tempDirectory = System.getProperty("java.io.tmpdir");
071: }
072: File tempDir = new File(tempDirectory);
073: log.info("Upload temporary directory set to: " + tempDir);
074: if (!tempDir.isDirectory()) {
075: log.fatal("Upload temporary directory is not a directory: "
076: + tempDir);
077: }
078: if (!tempDir.canWrite()) {
079: log
080: .fatal("Upload temporary directory cannot be written to: "
081: + tempDir);
082: }
083: }
084:
085: protected void doGet(HttpServletRequest request,
086: HttpServletResponse response) throws ServletException,
087: IOException {
088: // Send a '501 Not Implemented'
089: response.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED);
090: }
091:
092: protected void doPost(HttpServletRequest request,
093: HttpServletResponse response) throws ServletException,
094: IOException {
095: // Create the Deposit request
096: Deposit d = new Deposit();
097: Date date = new Date();
098: log.debug("Starting deposit processing at " + date.toString()
099: + " by " + request.getRemoteAddr());
100:
101: // Are there any authentication details?
102: String usernamePassword = getUsernamePassword(request);
103: if ((usernamePassword != null)
104: && (!usernamePassword.equals(""))) {
105: int p = usernamePassword.indexOf(":");
106: if (p != -1) {
107: d.setUsername(usernamePassword.substring(0, p));
108: d.setPassword(usernamePassword.substring(p + 1));
109: }
110: } else if (authenticateWithBasic()) {
111: String s = "Basic realm=\"SWORD\"";
112: response.setHeader("WWW-Authenticate", s);
113: response.setStatus(401);
114: return;
115: }
116:
117: // Do the processing
118: try {
119: // Write the file to the temp directory
120: // TODO: Improve the filename creation
121: String filename = tempDirectory + "SWORD-"
122: + request.getRemoteAddr() + "-" + counter++;
123: InputStream inputStream = request.getInputStream();
124: OutputStream outputStream = new FileOutputStream(filename);
125: int data;
126: while ((data = inputStream.read()) != -1) {
127: outputStream.write(data);
128: }
129: inputStream.close();
130: outputStream.close();
131:
132: // Check the MD5 hash
133: String receivedMD5 = ChecksumUtils.generateMD5(filename);
134: log.debug("Received filechecksum: " + receivedMD5);
135: d.setMd5(receivedMD5);
136: String md5 = request.getHeader("Content-MD5");
137: log.debug("Received file checksum header: " + md5);
138: if ((md5 != null) && (!md5.equals(receivedMD5))) {
139: response
140: .sendError(HttpServletResponse.SC_PRECONDITION_FAILED);
141: response.setHeader(HttpHeaders.X_ERROR_CODE,
142: "ErrorChecksumMismatch");
143: log
144: .debug("Bad MD5 for file. Aborting with appropriate error message");
145: } else {
146: // Set the file
147: File f = new File(filename);
148: FileInputStream fis = new FileInputStream(f);
149: d.setFile(fis);
150:
151: // Set the X-On-Behalf-Of header
152: d.setOnBehalfOf(request
153: .getHeader(HttpHeaders.X_ON_BEHALF_OF
154: .toString()));
155:
156: // Set the X-Format-Namespace header
157: d.setFormatNamespace(request
158: .getHeader(HttpHeaders.X_FORMAT_NAMESPACE));
159:
160: // Set the X-No-Op header
161: String noop = request.getHeader(HttpHeaders.X_NO_OP);
162: if ((noop != null) && (noop.equals("true"))) {
163: d.setNoOp(true);
164: } else {
165: d.setNoOp(false);
166: }
167:
168: // Set the X-Verbose header
169: String verbose = request
170: .getHeader(HttpHeaders.X_VERBOSE);
171: if ((verbose != null) && (verbose.equals("true"))) {
172: d.setVerbose(true);
173: } else {
174: d.setVerbose(false);
175: }
176:
177: // Set the slug
178: String slug = request.getHeader(HttpHeaders.SLUG);
179: if (slug != null) {
180: d.setSlug(slug);
181: }
182:
183: // Set the content disposition
184: d.setFilename(request
185: .getHeader(HttpHeaders.CONTENT_DISPOSITION));
186:
187: // Set the IP address
188: d.setIPAddress(request.getRemoteAddr());
189:
190: // Set the deposit location
191: d.setLocation(getUrl(request));
192:
193: // Set the content type
194: d.setContentType(request.getContentType());
195:
196: // Set the content length
197: String cl = request
198: .getHeader(HttpHeaders.CONTENT_LENGTH);
199: if ((cl != null) && (!cl.equals(""))) {
200: d.setContentLength(Integer.parseInt(cl));
201: }
202:
203: // Get the DepositResponse
204: DepositResponse dr = myRepository.doDeposit(d);
205:
206: // Print out the Deposit Response
207: response.setStatus(dr.getHttpResponse());
208: // response.setContentType("application/atomserv+xml");
209: response.setContentType("application/xml");
210: PrintWriter out = response.getWriter();
211: out.write(dr.marshall());
212: out.flush();
213:
214: // Close the input stream if it still open
215: fis.close();
216:
217: // Try deleting the temp file
218: f = new File(filename);
219: f.delete();
220: }
221: } catch (SWORDAuthenticationException sae) {
222: // Ask for credentials
223: if (authN.equals("Basic")) {
224: String s = "Basic realm=\"SWORD\"";
225: response.setHeader("WWW-Authenticate", s);
226: response.setStatus(401);
227: }
228: } catch (SWORDContentTypeException scte) {
229: // Throw a 415
230: response
231: .sendError(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE);
232: } catch (SWORDException se) {
233: // Throw a HTTP 500
234: response
235: .sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
236:
237: // Is there an appropriate error header to return?
238: if (se.getErrorCode() != null) {
239: response.setHeader(HttpHeaders.X_ERROR_CODE, se
240: .getErrorCode());
241: }
242: System.out.println(se.toString());
243: log.error(se.toString());
244: } catch (IOException ioe) {
245: response
246: .sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
247: log.error(ioe.toString());
248: } catch (NoSuchAlgorithmException nsae) {
249: response
250: .sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
251: log.error(nsae.toString());
252: }
253: }
254:
255: /**
256: * Utiliy method to return the username and password (separated by a colon ':')
257: *
258: * @param request
259: * @return The username and password combination
260: */
261: private String getUsernamePassword(HttpServletRequest request) {
262: try {
263: String authHeader = request.getHeader("Authorization");
264: if (authHeader != null) {
265: StringTokenizer st = new StringTokenizer(authHeader);
266: if (st.hasMoreTokens()) {
267: String basic = st.nextToken();
268: if (basic.equalsIgnoreCase("Basic")) {
269: String credentials = st.nextToken();
270: String userPass = new String(Base64
271: .decodeBase64(credentials.getBytes()));
272: return userPass;
273: }
274: }
275: }
276: } catch (Exception e) {
277: log.debug(e.toString());
278: }
279: return null;
280: }
281:
282: /**
283: * Utility method to deicde if we are using HTTP Basic authentication
284: *
285: * @return if HTTP Basic authentication is in use or not
286: */
287: private boolean authenticateWithBasic() {
288: if (authN.equalsIgnoreCase("Basic")) {
289: return true;
290: } else {
291: return false;
292: }
293: }
294:
295: /**
296: * Utility method to construct the URL called for this Servlet
297: *
298: * @param req The request object
299: * @return The URL
300: */
301: private static String getUrl(HttpServletRequest req) {
302: String reqUrl = req.getRequestURL().toString();
303: String queryString = req.getQueryString(); // d=789
304: if (queryString != null) {
305: reqUrl += "?" + queryString;
306: }
307: return reqUrl;
308: }
309: }
|