/**
* Filename: ImageHandler.java (in org.redpin.server.standalone.net)
* This file is part of the Redpin project.
*
* Redpin is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Redpin is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Redpin. If not, see <http://www.gnu.org/licenses/>.
*
* (c) Copyright ETH Zurich, Pascal Brogle, Philipp Bolliger, 2010, ALL RIGHTS RESERVED.
*
* www.redpin.org
*/
package org.redpin.server.standalone.net;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.MessageDigest;
import java.util.Random;
import java.util.StringTokenizer;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.redpin.server.standalone.util.Configuration;
import org.redpin.server.standalone.util.Log;
/**
* Handler for HTTP requests used to up- and download image.
*
* @author Pascal Brogle (broglep@student.ethz.ch)
*
*/
public class ImageHandler {
private DataInputStream in;
private DataOutputStream out;
final static String CRLF = "\r\n";
final static int HTTP_OK = 200;
final static int HTTP_NOT_FOUND = 404;
final static int BUFFER_SIZE = 1024;
private Logger log = Log.getLogger();
public ImageHandler(DataInputStream in, DataOutputStream out) {
this.in = in;
this.out = out;
}
public void handle(String firstLine) {
System.out.println(firstLine);
StringTokenizer tokenizer = new StringTokenizer(firstLine);
String httpMethod = tokenizer.nextToken();
String httpQuery = tokenizer.nextToken();
if (httpMethod.equals("GET")) {
requestGET(httpQuery);
}
if (httpMethod.equals("POST")) {
requestPOST(firstLine);
}
}
public void requestGET(String httpQuery) {
if (!httpQuery.equals("/")) {
String fileName = httpQuery.substring(1);
File f = new File(Configuration.ImageUploadPath + "/" + fileName);
if (f.exists() && f.canRead()) {
Log.getLogger().log(Level.FINER,
"sending map image " + fileName);
sendResponse(HTTP_OK, fileName, true);
} else {
Log.getLogger().log(Level.FINE,
"client requested non existing map image " + fileName);
sendResponse(HTTP_NOT_FOUND);
}
} else {
sendResponse(HTTP_NOT_FOUND);
}
}
@SuppressWarnings("deprecation")
public void requestPOST(String firstLine) {
String currentLine;
int dataLength = 0;
String boundary = "";
DataOutputStream fileout = null;
try {
while (true) {
currentLine = in.readLine();
if (currentLine == null) {
break;
}
//convert to lowercase, due to a bug in android http://code.google.com/p/android/issues/detail?id=6684
if (currentLine.toLowerCase().indexOf("Content-Type: multipart/form-data".toLowerCase()) != -1) {
boundary = currentLine.split("boundary=")[1];
break;
}
}
while (true) {
currentLine = in.readLine();
if (currentLine == null) {
break;
}
if (currentLine.indexOf("--" + boundary) != -1) {
String filename = in.readLine().split("filename=")[1]
.replaceAll("\"", "");
String[] filelist = filename.split("\\"
+ System.getProperty("file.separator"));
filename = filelist[filelist.length - 1];
if (!filename.equals("redpinfile")) {
Log
.getLogger()
.log(Level.FINE,
"unauthorized client tried to upload map image");
sendResponse(HTTP_NOT_FOUND);
}
break;
}
}
while (true) {
currentLine = in.readLine();
if (currentLine == null) {
break;
}
if (currentLine.indexOf("Content-Length:") != -1) {
dataLength = Integer.parseInt(currentLine.split(" ")[1]);
break;
}
}
// skip CRLF
in.readLine();
String fileName = generateFilename();
fileout = new DataOutputStream(new FileOutputStream(
Configuration.ImageUploadPath + "/" + fileName));
byte[] buf = new byte[BUFFER_SIZE];
int l = dataLength;
int bytesRead;
while (((bytesRead = in.read(buf,0,Math.min(BUFFER_SIZE, l))) != -1) && (l != 0)) {
fileout.write(buf, 0, bytesRead);
l -= bytesRead;
}
in.readLine();
currentLine = in.readLine();
if (currentLine != null && currentLine.indexOf("--" + boundary + "--") != -1) {
// everything ok
}
log.log(Level.FINER,
"successful uploaded map image " + fileName);
sendResponse(HTTP_OK, fileName, false);
} catch (IOException e) {
log.log(Level.WARNING, e.getMessage(), e);
} finally {
if (fileout != null) {
try {
fileout.close();
} catch (IOException e) {}
}
}
}
public void sendResponse(int code) {
sendResponse(code, null, false);
}
public void sendResponse(int code, String fileName, boolean isFile) {
String status = "";
String contentLength = "";
String contentType = "";
String responseBody = "";
FileInputStream fileInput;
DataInputStream fileReader = null;
if (code == HTTP_OK) {
status = "HTTP/1.1 200 OK" + CRLF;
}
if (code == HTTP_NOT_FOUND) {
status = "HTTP/1.1 404 Not Found" + CRLF;
}
if (isFile) {
try {
fileInput = new FileInputStream(Configuration.ImageUploadPath
+ "/" + fileName);
fileReader = new DataInputStream(fileInput);
int available = fileInput.available();
contentLength = "Content-Length: " + available + CRLF;
contentType = "Content-Type: application/octet-stream" + CRLF;
} catch (FileNotFoundException e) {
log.log(Level.WARNING, e.getMessage(), e);
} catch (IOException e) {
log.log(Level.WARNING, e.getMessage(), e);
}
} else {
if (fileName != null) {
responseBody = "http://{HOST}:{PORT}/" + fileName;
}
contentType = "Content-Type: text/plain" + CRLF;
contentLength = "Content-Length: " + responseBody.length() + CRLF;
}
try {
out.write(toByte(status));
out.write(toByte(contentType));
out.write(toByte(contentLength));
out.write(toByte("Connection: close" + CRLF));
out.write(toByte(CRLF));
if (isFile) {
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = fileReader.read(buffer)) != -1) {
out.write(buffer, 0, bytesRead);
}
} else {
out.write(toByte(responseBody));
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fileReader != null) {
try {
fileReader.close();
} catch (IOException e) {
}
}
}
}
private static byte[] toByte(String s) {
if (s == null || s.isEmpty()) {
return new byte[0];
}
byte[] r = null;
try {
r = s.getBytes("US-ASCII");
} catch (Exception e) {
r = s.getBytes();
}
return r;
}
public String generateFilename() {
MessageDigest md;
byte[] sha1hash = new byte[40];
Random r = new Random();
String fileName = "";
String token = "";
while (true) {
token = Long.toString(Math.abs(r.nextLong()), 36)
+ Long.toString(System.currentTimeMillis());
try {
md = MessageDigest.getInstance("SHA-1");
md.update(token.getBytes("iso-8859-1"), 0, token.length());
sha1hash = md.digest();
} catch (Exception e) {
log.log(Level.WARNING, e.getMessage(), e);
}
fileName = convertToHex(sha1hash);
if (!new File(Configuration.ImageUploadPath + fileName).exists()) {
break;
}
}
return fileName;
}
private static String convertToHex(byte[] data) {
StringBuffer buffer = new StringBuffer();
for (int i = 0; i < data.length; i++) {
int halfbyte = (data[i] >>> 4) & 0x0F;
int two_halfs = 0;
do {
if ((0 <= halfbyte) && (halfbyte <= 9)) {
buffer.append((char) ('0' + halfbyte));
} else {
buffer.append((char) ('a' + (halfbyte - 10)));
}
halfbyte = data[i] & 0x0F;
} while (two_halfs++ < 1);
}
return buffer.toString();
}
}