/*
* Copyright 2012 The Stanford MobiSocial Laboratory
*
* Licensed under the Apache 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.apache.org/licenses/LICENSE-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.mobisocial.corral;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.security.GeneralSecurityException;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.entity.AbstractHttpEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.mobisocial.corral.CorralDownloadHandler.CorralDownloadFuture;
import org.mobisocial.corral.CorralHelper.DownloadProgressCallback;
import org.mobisocial.corral.CorralHelper.EncRslt;
import org.mobisocial.corral.CorralHelper.UploadProgressCallback;
import org.mobisocial.corral.CorralHelper.UploadProgressCallback.UploadState;
import android.content.Context;
import android.net.Uri;
import android.util.Log;
public class CorralS3Connector {
private static final String CONTENT_TYPE = "application/octet-stream; charset=UTF-8";
private String TAG = "CorralS3Connector";
private Context mContext;
private static final String SERVER_URL = "http://corral-1.s3.amazonaws.com/";
public CorralS3Connector(Context context){
mContext = context;
}
public void uploadToServer(String ticket, final EncRslt rslt, String datestr, String objName,
final UploadProgressCallback callback) throws ClientProtocolException, IOException {
callback.onProgress(UploadState.PREPARING_UPLOAD, 0);
Log.d(TAG, "-----UPLOAD START-----");
Log.d(TAG, "URI: "+rslt.uri.toString());
Log.d(TAG, "length: "+rslt.length);
Log.d(TAG, "ticket: "+ticket);
Log.d(TAG, SERVER_URL+objName);
Log.d(TAG, "Authorization: AWS "+ticket);
Log.d(TAG, "Content-Md5: "+rslt.md5);
Log.d(TAG, "Content-Type: "+CONTENT_TYPE);
Log.d(TAG, "Date: "+datestr);
InputStream in = mContext.getContentResolver().openInputStream(rslt.uri);
final int contentLength = in.available();
HttpClient http = new DefaultHttpClient();
HttpPut put = new HttpPut(SERVER_URL + objName);
put.setHeader("Authorization", "AWS "+ticket);
put.setHeader("Content-Md5", rslt.md5);
put.setHeader("Content-Type", CONTENT_TYPE);
put.setHeader("Date", datestr);
HttpEntity progress = new ProgressEntity(callback, rslt, contentLength);
put.setEntity(progress);
HttpResponse response = http.execute(put);
final int responseCode = response.getStatusLine().getStatusCode();
// FOR DEBUG
if(responseCode != HttpURLConnection.HTTP_OK){
throw new RuntimeException("invalid response code, " + responseCode);
}
}
Uri downloadAndDecrypt(String ticket, String datestr, String objName,
File cachefile, String mykey,
CorralDownloadFuture future, DownloadProgressCallback callback) throws IOException, GeneralSecurityException {
Log.d(TAG, "-----DOWNLOAD+DECRYPT START-----"+(String.valueOf(System.currentTimeMillis())));
Log.d(TAG, SERVER_URL+objName);
Log.d(TAG, "Authorization: AWS "+ticket);
Log.d(TAG, "Date: "+datestr);
HttpClient http = new DefaultHttpClient();
HttpGet get = new HttpGet(SERVER_URL+objName);
get.addHeader("Authorization", "AWS " + ticket);
get.addHeader("Date", datestr);
HttpResponse response = http.execute(get);
DataInputStream is = new DataInputStream(response.getEntity().getContent());
long contentLength = response.getEntity().getContentLength();
if (!cachefile.exists()) {
File tmpFile = new File(cachefile.getAbsoluteFile() + ".tmp");
tmpFile.getParentFile().mkdirs();
try {
CryptUtil cu = new CryptUtil(mykey);
cu.InitCiphers();
FileOutputStream fos = new FileOutputStream(tmpFile);
cu.decrypt(is, fos, contentLength, callback);
try {
is.close();
} catch (IOException e) {}
try {
fos.close();
} catch (IOException e) {}
tmpFile.renameTo(cachefile);
} catch (IOException e) {
if (tmpFile.exists()) {
tmpFile.delete();
}
throw e;
} catch (GeneralSecurityException e) {
throw e;
}
}
return cachefile.exists() ? Uri.fromFile(cachefile) : null;
}
class ProgressEntity extends AbstractHttpEntity {
int uploaded = 0;
int progress = 0;
final UploadProgressCallback callback;
final EncRslt rslt;
final int contentLength;
long mLastCheckedForCancellation = 0;
public ProgressEntity(UploadProgressCallback callback, EncRslt encoding, int contentLength) {
this.callback = callback;
this.rslt = encoding;
this.contentLength = contentLength;
}
@Override
public void writeTo(OutputStream outstream) throws IOException {
callback.onProgress(UploadState.TRANSFER_IN_PROGRESS, progress);
if (outstream == null) {
throw new IllegalArgumentException("Output stream may not be null");
}
checkUserCancellation();
InputStream instream = getContent();
try {
checkUserCancellation();
byte[] tmp = new byte[4096];
int l;
while ((l = instream.read(tmp)) != -1) {
checkUserCancellation();
outstream.write(tmp, 0, l);
uploaded += l;
int newProgress = (int) Math.round(100. * uploaded / contentLength);
if (newProgress > progress) {
progress = newProgress;
callback.onProgress(UploadState.TRANSFER_IN_PROGRESS, progress);
}
}
outstream.flush();
} finally {
instream.close();
}
callback.onProgress(UploadState.FINISHING_UP, 0);
}
void checkUserCancellation() throws IOException {
long now = System.currentTimeMillis();
if (now - mLastCheckedForCancellation > CorralHelper.CANCEL_SAMPLE_WINDOW) {
if (callback.isCancelled()) {
throw new IOException("User cancelled transfer");
}
mLastCheckedForCancellation = now;
}
}
@Override
public boolean isStreaming() {
return false;
}
@Override
public boolean isRepeatable() {
return true;
}
@Override
public long getContentLength() {
return contentLength;
}
@Override
public InputStream getContent() throws IOException, IllegalStateException {
return mContext.getContentResolver().openInputStream(rslt.uri);
}
};
}