package org.awesomeapp.messenger.crypto.otr;
import cz.msebera.android.httpclient.Header;
import cz.msebera.android.httpclient.HttpException;
import cz.msebera.android.httpclient.HttpMessage;
import cz.msebera.android.httpclient.HttpRequest;
import cz.msebera.android.httpclient.HttpRequestFactory;
import cz.msebera.android.httpclient.HttpResponse;
import cz.msebera.android.httpclient.HttpResponseFactory;
import cz.msebera.android.httpclient.MethodNotSupportedException;
import cz.msebera.android.httpclient.ProtocolVersion;
import cz.msebera.android.httpclient.RequestLine;
import cz.msebera.android.httpclient.impl.DefaultHttpResponseFactory;
import cz.msebera.android.httpclient.impl.io.AbstractSessionInputBuffer;
import cz.msebera.android.httpclient.impl.io.AbstractSessionOutputBuffer;
import cz.msebera.android.httpclient.impl.io.HttpRequestParser;
import cz.msebera.android.httpclient.impl.io.HttpRequestWriter;
import cz.msebera.android.httpclient.impl.io.HttpResponseParser;
import cz.msebera.android.httpclient.impl.io.HttpResponseWriter;
import cz.msebera.android.httpclient.io.HttpMessageWriter;
import cz.msebera.android.httpclient.io.SessionInputBuffer;
import cz.msebera.android.httpclient.message.BasicHttpRequest;
import cz.msebera.android.httpclient.message.BasicHttpResponse;
import cz.msebera.android.httpclient.message.BasicLineFormatter;
import cz.msebera.android.httpclient.message.BasicLineParser;
import cz.msebera.android.httpclient.message.BasicStatusLine;
import cz.msebera.android.httpclient.message.LineFormatter;
import cz.msebera.android.httpclient.message.LineParser;
import cz.msebera.android.httpclient.params.BasicHttpParams;
import cz.msebera.android.httpclient.params.HttpParams;
import info.guardianproject.iocipher.File;
import info.guardianproject.iocipher.FileInputStream;
import info.guardianproject.iocipher.RandomAccessFile;
import org.awesomeapp.messenger.service.IDataListener;
import org.awesomeapp.messenger.ImApp;
import org.awesomeapp.messenger.util.SecureMediaStore;
import org.awesomeapp.messenger.model.Address;
import org.awesomeapp.messenger.model.ChatSession;
import org.awesomeapp.messenger.model.DataHandler;
import org.awesomeapp.messenger.model.Message;
import org.awesomeapp.messenger.util.Debug;
import org.awesomeapp.messenger.util.LogCleaner;
import org.awesomeapp.messenger.util.SystemServices;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.security.DigestInputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.UUID;
import net.java.otr4j.session.SessionStatus;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.output.NullOutputStream;
import android.net.Uri;
import android.os.RemoteException;
import android.util.Log;
public class OtrDataHandler implements DataHandler {
public static final String URI_PREFIX_OTR_IN_BAND = "otr-in-band:/storage/";
private static final int MAX_OUTSTANDING = 3;
private static final int MAX_CHUNK_LENGTH = 32768;
private static final int REQUEST_CHUNK_LENGTH = 1024*8;
private static final int MAX_TRANSFER_LENGTH = 1024*1024*10; //10MB max file size
private static final byte[] EMPTY_BODY = new byte[0];
private static final String TAG = "Zom.Data";
private static final ProtocolVersion PROTOCOL_VERSION = new ProtocolVersion("HTTP", 1, 1);
private static HttpParams params = new BasicHttpParams();
private static HttpRequestFactory requestFactory = new MyHttpRequestFactory();
private static HttpResponseFactory responseFactory = new DefaultHttpResponseFactory();
private LineParser lineParser = new BasicLineParser(PROTOCOL_VERSION);
private LineFormatter lineFormatter = new BasicLineFormatter();
private ChatSession mChatSession;
private long mChatId;
private IDataListener mDataListener;
private SessionStatus mOtrStatus;
HashMap<String, Offer> offerCache = new HashMap<>();//CacheBuilder.newBuilder().maximumSize(100).build();
HashMap<String, Request> requestCache = new HashMap<>();//CacheBuilder.newBuilder().maximumSize(100).build();
HashMap<String, Transfer> transferCache = new HashMap<>();//CacheBuilder.newBuilder().maximumSize(100).build();
public OtrDataHandler(ChatSession chatSession) {
this.mChatSession = chatSession;
}
public void setChatId(long chatId) {
this.mChatId = chatId;
}
public void onOtrStatusChanged(SessionStatus status) {
mOtrStatus = status;
if (status == SessionStatus.ENCRYPTED) {
retryRequests();
}
}
private synchronized void retryRequests() {
// Resend all unfilled requests
Collection<Request> requests = new ArrayList<Request>(requestCache.values());
for (Request request: requests) {
if (!request.isSeen())
sendRequest(request);
}
}
public void setDataListener (IDataListener dataListener)
{
mDataListener = dataListener;
}
public static class MyHttpRequestFactory implements HttpRequestFactory {
public MyHttpRequestFactory() {
super();
}
public HttpRequest newHttpRequest(final RequestLine requestline)
throws MethodNotSupportedException {
if (requestline == null) {
throw new IllegalArgumentException("Request line may not be null");
}
//String method = requestline.getMethod();
return new BasicHttpRequest(requestline);
}
public HttpRequest newHttpRequest(final String method, final String uri)
throws MethodNotSupportedException {
return new BasicHttpRequest(method, uri);
}
}
static class MemorySessionInputBuffer extends AbstractSessionInputBuffer {
public MemorySessionInputBuffer(byte[] value) {
init(new ByteArrayInputStream(value), 1000, params);
}
@Override
public boolean isDataAvailable(int timeout) throws IOException {
throw new UnsupportedOperationException();
}
}
static class MemorySessionOutputBuffer extends AbstractSessionOutputBuffer {
ByteArrayOutputStream outputStream;
public MemorySessionOutputBuffer() {
outputStream = new ByteArrayOutputStream(1000);
init(outputStream, 1000, params);
}
public byte[] getOutput() {
return outputStream.toByteArray();
}
}
public synchronized void onIncomingRequest(Address requestThem, Address requestUs, byte[] value) {
//Log.e( TAG, "onIncomingRequest:" + requestThem);
SessionInputBuffer inBuf = new MemorySessionInputBuffer(value);
HttpRequestParser parser = new HttpRequestParser(inBuf, lineParser, requestFactory, params);
HttpRequest req;
try {
req = (HttpRequest)parser.parse();
} catch (IOException e) {
throw new RuntimeException(e);
} catch (HttpException e) {
e.printStackTrace();
return;
}
String requestMethod = req.getRequestLine().getMethod();
String uid = req.getFirstHeader("Request-Id").getValue();
String url = req.getRequestLine().getUri();
if (requestMethod.equals("OFFER")) {
debug("incoming OFFER " + url);
for (Header header :req.getAllHeaders())
{
debug("incoming header: " + header.getName() + "=" + header.getValue());
}
if (!url.startsWith(URI_PREFIX_OTR_IN_BAND)) {
debug("Unknown url scheme " + url);
sendResponse(requestUs, requestThem, 400, "Unknown scheme", uid, EMPTY_BODY);
return;
}
if (!req.containsHeader("File-Length"))
{
sendResponse(requestUs, requestThem, 400, "File-Length must be supplied", uid, EMPTY_BODY);
return;
}
int length = Integer.parseInt(req.getFirstHeader("File-Length").getValue());
if (!req.containsHeader("File-Hash-SHA1"))
{
sendResponse(requestUs, requestThem, 400, "File-Hash-SHA1 must be supplied", uid, EMPTY_BODY);
return;
}
sendResponse(requestUs, requestThem, 200, "OK", uid, EMPTY_BODY);
String sum = req.getFirstHeader("File-Hash-SHA1").getValue();
String type = null;
if (req.containsHeader("Mime-Type")) {
type = req.getFirstHeader("Mime-Type").getValue();
}
debug("Incoming sha1sum " + sum);
Transfer transfer;
try {
transfer = new VfsTransfer(url, type, length, requestUs, requestThem, sum);
} catch (IOException e) {
e.printStackTrace();
return;
}
transferCache.put(url, transfer);
// Handle offer
// TODO ask user to confirm we want this
boolean accept = false;
if (mDataListener != null)
{
try {
mDataListener.onTransferRequested(url, requestThem.getAddress(),requestUs.getAddress(),transfer.url);
//callback is now async, via "acceptTransfer" method
// if (accept)
// transfer.perform();
} catch (RemoteException e) {
LogCleaner.error(ImApp.LOG_TAG, "error approving OTRDATA transfer request", e);
}
}
} else if (requestMethod.equals("GET") && url.startsWith(URI_PREFIX_OTR_IN_BAND)) {
debug("incoming GET " + url);
ByteArrayOutputStream byteBuffer = new ByteArrayOutputStream();
int reqEnd;
try {
Offer offer = offerCache.get(url);
if (offer == null) {
sendResponse(requestUs, requestThem,400, "No such offer made", uid, EMPTY_BODY);
return;
}
offer.seen(); // in case we don't see a response to underlying request, but peer still proceeds
if (!req.containsHeader("Range"))
{
sendResponse(requestUs, requestThem, 400, "Range must start with bytes=", uid, EMPTY_BODY);
return;
}
String rangeHeader = req.getFirstHeader("Range").getValue();
String[] spec = rangeHeader.split("=");
if (spec.length != 2 || !spec[0].equals("bytes"))
{
sendResponse(requestUs, requestThem, 400, "Range must start with bytes=", uid, EMPTY_BODY);
return;
}
String[] startEnd = spec[1].split("-");
if (startEnd.length != 2)
{
sendResponse(requestUs, requestThem, 400, "Range must be START-END", uid, EMPTY_BODY);
return;
}
int start = Integer.parseInt(startEnd[0]);
int end = Integer.parseInt(startEnd[1]);
if (end - start + 1 > MAX_CHUNK_LENGTH) {
sendResponse(requestUs, requestThem, 400, "Range must be at most " + MAX_CHUNK_LENGTH, uid, EMPTY_BODY);
return;
}
File fileGet = new File(offer.getUri());
long fileLength = -1;
if (fileGet.exists()) {
fileLength = fileGet.length();
FileInputStream is = new FileInputStream(fileGet);
readIntoByteBuffer(byteBuffer, is, start, end);
is.close();
}
else
{
java.io.File fileGetExtern = new java.io.File(offer.getUri());
if (fileGetExtern.exists()) {
fileLength = fileGetExtern.length();
java.io.FileInputStream is = new java.io.FileInputStream(fileGetExtern);
readIntoByteBuffer(byteBuffer, is, start, end);
is.close();
}
}
if (mDataListener != null && fileLength != -1)
{
float percent = ((float)end) / ((float)fileLength);
mDataListener.onTransferProgress(true, offer.getId(), requestThem.getAddress(), offer.getUri(),
percent);
String mimeType = null;
if (req.getFirstHeader("Mime-Type") != null)
mimeType = req.getFirstHeader("Mime-Type").getValue();
mDataListener.onTransferComplete(true, offer.getId(), requestThem.getAddress(), offer.getUri(), mimeType, offer.getUri());
}
} catch (UnsupportedEncodingException e) {
// throw new RuntimeException(e);
sendResponse(requestUs, requestThem,400, "Unsupported encoding", uid, EMPTY_BODY);
return;
} catch (IOException e) {
//throw new RuntimeException(e);
sendResponse(requestUs,requestThem, 400, "IOException", uid, EMPTY_BODY);
return;
} catch (NumberFormatException e) {
sendResponse(requestUs, requestThem,400, "Range is not numeric", uid, EMPTY_BODY);
return;
} catch (Exception e) {
sendResponse(requestUs, requestThem,500, "Unknown error", uid, EMPTY_BODY);
return;
}
byte[] body = byteBuffer.toByteArray();
// debug("Sent sha1 is " + sha1sum(body));
sendResponse(requestUs, requestThem, 200, "OK", uid, body);
} else {
debug("Unknown method / url " + requestMethod + " " + url);
sendResponse(requestUs, requestThem, 400, "OK", uid, EMPTY_BODY);
}
}
public void acceptTransfer (String url, String address)
{
Transfer transfer = transferCache.get(url);
if (transfer != null)
{
transfer.perform();
}
}
private static void readIntoByteBuffer(ByteArrayOutputStream byteBuffer, InputStream is, int start, int end)
throws IOException {
//Log.e( TAG, "readIntoByteBuffer:" + (end-start));
if (start != is.skip(start)) {
return;
}
int size = end - start + 1;
int buffersize = 1024;
byte[] buffer = new byte[buffersize];
int len = 0;
while((len = is.read(buffer)) != -1){
if (len > size) {
len = size;
}
byteBuffer.write(buffer, 0, len);
size -= len;
}
}
private static void readIntoByteBuffer(ByteArrayOutputStream byteBuffer, SessionInputBuffer sib)
throws IOException {
//Log.e( TAG, "readIntoByteBuffer:");
int buffersize = 1024;
byte[] buffer = new byte[buffersize];
int len = 0;
while((len = sib.read(buffer)) != -1){
byteBuffer.write(buffer, 0, len);
}
}
private void sendResponse(Address us, Address them, int code, String statusString, String uid, byte[] body) {
MemorySessionOutputBuffer outBuf = new MemorySessionOutputBuffer();
HttpMessageWriter writer = new HttpResponseWriter(outBuf, lineFormatter, params);
HttpMessage response = new BasicHttpResponse(new BasicStatusLine(PROTOCOL_VERSION, code, statusString));
response.addHeader("Request-Id", uid);
try {
writer.write(response);
outBuf.write(body);
outBuf.flush();
} catch (IOException e) {
throw new RuntimeException(e);
} catch (HttpException e) {
throw new RuntimeException(e);
}
byte[] data = outBuf.getOutput();
Message message = new Message("");
message.setFrom(us);
message.setTo(them);
debug("send response " + statusString + " for " + uid);
mChatSession.sendDataAsync(message, true, data);
}
public void onIncomingResponse(Address from, Address to, byte[] value) {
//Log.e( TAG, "onIncomingResponse:" + value.length);
SessionInputBuffer buffer = new MemorySessionInputBuffer(value);
HttpResponseParser parser = new HttpResponseParser(buffer, lineParser, responseFactory, params);
HttpResponse res;
try {
res = (HttpResponse) parser.parse();
} catch (IOException e) {
throw new RuntimeException(e);
} catch (HttpException e) {
e.printStackTrace();
return;
}
String uid = res.getFirstHeader("Request-Id").getValue();
Request request = requestCache.get(uid);
if (request == null) {
debug("Unknown request ID " + uid);
return;
}
if (request.isSeen()) {
debug("Already seen request ID " + uid);
return;
}
request.seen();
int statusCode = res.getStatusLine().getStatusCode();
if (statusCode != 200) {
debug("got status " + statusCode + ": " + res.getStatusLine().getReasonPhrase());
// TODO handle error
return;
}
// TODO handle success
try {
ByteArrayOutputStream byteBuffer = new ByteArrayOutputStream();
readIntoByteBuffer(byteBuffer, buffer);
// debug("Received sha1 @" + request.start + " is " + sha1sum(byteBuffer.toByteArray()));
if (request.method.equals("GET")) {
Transfer transfer = transferCache.get(request.url);
if (transfer == null) {
debug("Transfer expired for url " + request.url);
return;
}
transfer.chunkReceived(request, byteBuffer.toByteArray());
if (transfer.isDone()) {
//Log.e( TAG, "onIncomingResponse: isDone");
debug("Transfer complete for " + request.url);
String filename = transfer.closeFile();
Uri vfsUri = SecureMediaStore.vfsUri(filename);
if (transfer.checkSum()) {
//Log.e( TAG, "onIncomingResponse: writing");
if (mDataListener != null)
mDataListener.onTransferComplete(
false,
null,
from.getAddress(),
transfer.url,
transfer.type,
vfsUri.toString());
} else {
if (mDataListener != null)
mDataListener.onTransferFailed(
false,
null,
to.getAddress(),
transfer.url,
"checksum");
debug( "Wrong checksum for file");
}
} else {
if (mDataListener != null)
mDataListener.onTransferProgress(true, null, to.getAddress(), transfer.url,
((float)transfer.chunksReceived) / transfer.chunks);
transfer.perform();
debug("Progress " + transfer.chunksReceived + " / " + transfer.chunks);
}
}
} catch (IOException e) {
debug("Could not read line from response");
} catch (RemoteException e) {
debug("Could not read remote exception");
}
}
private String getFilenameFromUrl(String url) {
String[] path = url.split("/");
String sanitizedPath = SystemServices.sanitize(path[path.length - 1]);
return sanitizedPath;
}
/**
private File writeDataToStorage (String url, byte[] data)
{
debug( "writeDataToStorage:" + url + " " + data.length);
String[] path = url.split("/");
String sanitizedPath = SystemServices.sanitize(path[path.length - 1]);
File fileDownloadsDir = new File(Environment.DIRECTORY_DOWNLOADS);
fileDownloadsDir.mkdirs();
info.guardianproject.iocipher.File file = new info.guardianproject.iocipher.File(fileDownloadsDir, sanitizedPath);
debug( "writeDataToStorage:" + file.getAbsolutePath() );
try {
OutputStream output = (new info.guardianproject.iocipher.FileOutputStream(file));
output.write(data);
output.flush();
output.close();
return file;
} catch (IOException e) {
OtrDebugLogger.log("error writing file", e);
return null;
}
}*/
@Override
public void offerData(String id, Address us, Address them, String localUri, Map<String, String> headers) throws IOException {
// TODO stash localUri and intended recipient
long length = -1;
String hash = null;
File fileLocal = new File(localUri);
if (fileLocal.exists()) {
length = fileLocal.length();
if (length > MAX_TRANSFER_LENGTH) {
throw new IOException("Length too large: " + length);
}
FileInputStream is = new FileInputStream(fileLocal);
hash = sha1sum(is);
is.close();
}
else
{
//it is not in the encrypted store
java.io.File fileExtern = new java.io.File(localUri);
length = fileExtern.length();
if (length > MAX_TRANSFER_LENGTH) {
throw new IOException("Length too large: " + length);
}
java.io.FileInputStream is = new java.io.FileInputStream(fileExtern);
hash = sha1sum(is);
is.close();
}
if (headers == null)
headers = new HashMap<>();
headers.put("File-Name", fileLocal.getName());
headers.put("File-Length", String.valueOf(length));
headers.put("File-Hash-SHA1", hash);
if (!headers.containsKey("Mime-Type")) {
String mimeType = SystemServices.getMimeType(localUri);
headers.put("Mime-Type", mimeType);
}
/**
* 0 = {BufferedHeader@7083} "File-Name: B3C1B9F7-81EB-454A-AB5B-2B3B645453C6.m4a"
1 = {BufferedHeader@7084} "Mime-Type: audio/x-m4a"
2 = {BufferedHeader@7085} "File-Length: 0"
3 = {BufferedHeader@7086} "Request-Id: 92C2FF9A-7C1E-42BA-A5DA-F2D445BCF1A5"
*/
String[] paths = localUri.split("/");
String url = URI_PREFIX_OTR_IN_BAND + SystemServices.sanitize(paths[paths.length - 1]);
Request request = new Request("OFFER", us, them, url, headers);
offerCache.put(url, new Offer(id, localUri, request));
sendRequest(request);
}
public Request performGetData(Address us, Address them, String url, Map<String, String> headers, int start, int end) {
String rangeSpec = "bytes=" + start + "-" + end;
headers.put("Range", rangeSpec);
Request request = new Request("GET", us, them, url, start, end, headers, EMPTY_BODY);
sendRequest(request);
return request;
}
static class Offer {
private String mId;
private String mUri;
private Request request;
public Offer(String id, String uri, Request request) {
this.mId = id;
this.mUri = uri;
this.request = request;
}
public String getUri() {
return mUri;
}
public String getId() {
return mId;
}
public Request getRequest() {
return request;
}
public void seen() {
request.seen();
}
public boolean isSeen () { return request.isSeen(); }
}
static class Request {
public Request(String method, Address us, Address them, String url, int start, int end, Map<String, String> headers, byte[] body) {
this.method = method;
this.url = url;
this.start = start;
this.end = end;
this.us = us;
this.them = them;
this.headers = headers;
this.body = body;
}
public Request(String method, Address us, Address them, String url, Map<String, String> headers) {
this(method, us, them, url, -1, -1, headers, null);
}
public String method;
public String url;
public int start;
public int end;
public byte[] data;
public boolean seen = false;
public Address us;
public Address them;
public Map<String, String> headers;
public byte[] body;
public boolean isSeen() {
return seen;
}
public void seen() {
seen = true;
}
}
public class Transfer {
public final String TAG = Transfer.class.getSimpleName();
public String url;
public String type;
public int chunks = 0;
public int chunksReceived = 0;
private int length = 0;
private int current = 0;
private Address us;
private Address them;
protected Set<Request> outstanding;
private byte[] buffer;
protected String sum;
public Transfer(String url, String type, int length, Address us, Address them, String sum) {
this.url = url;
this.type = type;
this.length = length;
this.us = us;
this.them = them;
this.sum = sum;
//Log.e(TAG, "url:"+url + " type:"+ type + " length:"+length) ;
if (length > MAX_TRANSFER_LENGTH || length <= 0) {
throw new RuntimeException("Invalid transfer size " + length);
}
chunks = ((length - 1) / REQUEST_CHUNK_LENGTH) + 1;
buffer = new byte[length];
outstanding = new HashSet<Request>();
}
public boolean checkSum() {
return sum.equals(sha1sum(buffer));
}
public synchronized boolean perform() {
// TODO global throttle rather than this local hack
int performIdx = 0;
while (current < length && outstanding.size() < MAX_OUTSTANDING) {
Map<String, String> headers = new HashMap<>();
int end = Math.min(length, current + REQUEST_CHUNK_LENGTH)-1;
Request request= performGetData(us, them, url, headers, current, end);
outstanding.add(request);
current = end + 1;
// debug("current: " + current);
}
return true;
}
public boolean isDone() {
//Log.e( TAG, "isDone:" + chunksReceived + " " + chunks);
return chunksReceived == chunks;
}
public void chunkReceived(Request request, byte[] bs) {
//Log.e( TAG, "chunkReceived:" + bs.length);
chunksReceived++;
System.arraycopy(bs, 0, buffer, request.start, bs.length);
outstanding.remove(request);
}
public String getSum() {
return sum;
}
public String closeFile() throws IOException
{
return url;
}
}
public class VfsTransfer extends Transfer {
String localFilename;
private RandomAccessFile raf;
public VfsTransfer(String url, String type, int length, Address us, Address them, String sum) throws FileNotFoundException {
super(url, type, length, us, them, sum);
}
@Override
public void chunkReceived(Request request, byte[] bs) {
if (raf == null) {
if (!perform()) //initialize file
return;
}
try {
raf.seek(request.start);
raf.write(bs);
debug("chunkReceived: " + request.start + "-" + request.end);
chunksReceived++;
} catch (Exception e) {
debug("chunkReceived failure: " + request.start + "-" + request.end + ": " + e.toString());
}
outstanding.remove(request);
}
@Override
public boolean checkSum() {
try {
File file = new File(localFilename);
return sum.equals( checkSum(file.getAbsolutePath()) );
} catch (IOException e) {
debug("checksum IOException");
return false;
}
}
@Override
public synchronized boolean perform() {
try {
if (raf == null) {
raf = openFile(url);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
return false;
}
return super.perform();
}
private RandomAccessFile openFile(String url) throws FileNotFoundException {
debug( "openFile: url " + url) ;
String sessionId = ""+ mChatId;
String filename = getFilenameFromUrl(url);
localFilename = SecureMediaStore.getDownloadFilename(sessionId, filename);
debug( "openFile: localFilename " + localFilename) ;
info.guardianproject.iocipher.File fileNew = new info.guardianproject.iocipher.File(localFilename);
fileNew.getParentFile().mkdirs();
info.guardianproject.iocipher.RandomAccessFile ras = new info.guardianproject.iocipher.RandomAccessFile(localFilename, "rw");
return ras;
}
public String closeFile() throws IOException {
//Log.e(TAG, "closeFile") ;
raf.close();
File file = new File(localFilename);
String newPath = file.getCanonicalPath();
if(true) return newPath;
newPath = newPath.substring(0,newPath.length()-4); // remove the .tmp
//Log.e(TAG, "vfsCloseFile: rename " + newPath) ;
File newPathFile = new File(newPath);
boolean success = file.renameTo(newPathFile);
if (!success) {
throw new IOException("Rename error " + newPath );
}
return newPath;
}
private String checkSum(String filename) throws IOException {
FileInputStream fis = new FileInputStream(new File(filename));
String sum = sha1sum(fis);
fis.close();
return sum;
}
}
private void sendRequest(Request request) {
MemorySessionOutputBuffer outBuf = new MemorySessionOutputBuffer();
HttpMessageWriter writer = new HttpRequestWriter(outBuf, lineFormatter, params);
HttpMessage req = new BasicHttpRequest(request.method, request.url, PROTOCOL_VERSION);
String uid = UUID.randomUUID().toString();
req.addHeader("Request-Id", uid);
if (request.headers != null) {
for (Entry<String, String> entry : request.headers.entrySet()) {
req.addHeader(entry.getKey(), entry.getValue());
}
}
try {
writer.write(req);
outBuf.write(request.body);
outBuf.flush();
} catch (IOException e) {
throw new RuntimeException(e);
} catch (HttpException e) {
throw new RuntimeException(e);
}
byte[] data = outBuf.getOutput();
Message message = new Message("");
message.setFrom(request.us);
message.setTo(request.them);
if (req.containsHeader("Range"))
debug("send request " + request.method + " " + request.url + " " + req.getFirstHeader("Range"));
else
debug("send request " + request.method + " " + request.url);
requestCache.put(uid, request);
mChatSession.sendDataAsync(message, false, data);
}
private static String hexChr(int b) {
return Integer.toHexString(b & 0xF);
}
private static String toHex(int b) {
return hexChr((b & 0xF0) >> 4) + hexChr(b & 0x0F);
}
private String sha1sum(byte[] bytes) {
MessageDigest digest;
try {
digest = MessageDigest.getInstance("SHA1");
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
digest.update(bytes, 0, bytes.length);
byte[] sha1sum = digest.digest();
String display = "";
for(byte b : sha1sum)
display += toHex(b);
return display;
}
private String sha1sum(java.io.InputStream is) {
MessageDigest digest;
try {
digest = MessageDigest.getInstance("SHA1");
DigestInputStream dig = new DigestInputStream(is, digest);
IOUtils.copy( dig, new NullOutputStream() );
byte[] sha1sum = digest.digest();
String display = "";
for(byte b : sha1sum)
display += toHex(b);
return display;
}
catch (Exception npe)
{
Log.e(ImApp.LOG_TAG,"unable to hash file",npe);
return null;
}
}
private void debug (String msg)
{
if (Debug.DEBUG_ENABLED)
Log.d("OTRDATA",msg);
}
}