package i2p.bote.android.provider;
import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.net.Uri;
import android.os.ParcelFileDescriptor;
import android.provider.OpenableColumns;
import android.util.Log;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;
import javax.activation.MimetypesFileTypeMap;
import javax.mail.MessagingException;
import javax.mail.Part;
import i2p.bote.Util;
import i2p.bote.android.BuildConfig;
import i2p.bote.android.util.BoteHelper;
import i2p.bote.email.Email;
import i2p.bote.fileencryption.PasswordException;
public class AttachmentProvider extends ContentProvider {
public static final String AUTHORITY = BuildConfig.APPLICATION_ID + ".attachmentprovider";
private static final int RAW_ATTACHMENT = 1;
private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
static {
sUriMatcher.addURI(AUTHORITY, "*/*/#/RAW", RAW_ATTACHMENT);
}
private final static String[] OPENABLE_PROJECTION = {
OpenableColumns.DISPLAY_NAME, OpenableColumns.SIZE};
public static Uri getUriForAttachment(String folderName, String messageId, int partNum) {
return new Uri.Builder()
.scheme("content")
.authority(AUTHORITY)
.appendPath(folderName)
.appendPath(messageId)
.appendPath(Integer.toString(partNum))
.appendPath("RAW")
.build();
}
@Override
public boolean onCreate() {
return true;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
if (sUriMatcher.match(uri) == UriMatcher.NO_MATCH)
throw new IllegalArgumentException("Invalid URI: " + uri);
if (projection == null) {
projection = OPENABLE_PROJECTION;
}
final MatrixCursor cursor = new MatrixCursor(projection, 1);
MatrixCursor.RowBuilder b = cursor.newRow();
try {
Part attachment = getAttachment(uri);
if (attachment == null)
return null;
for (String col : projection) {
switch (col) {
case OpenableColumns.DISPLAY_NAME:
b.add(attachment.getFileName());
break;
case OpenableColumns.SIZE:
b.add(Util.getPartSize(attachment));
break;
}
}
} catch (PasswordException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (MessagingException e) {
e.printStackTrace();
}
return cursor;
}
@Override
public String getType(Uri uri) {
if (sUriMatcher.match(uri) != UriMatcher.NO_MATCH) {
try {
Part attachment = getAttachment(uri);
if (attachment != null) {
String contentType = attachment.getContentType();
// Remove any "; name=fileName" suffix
int delim = contentType.indexOf(';');
if (delim >= 0) {
String params = contentType.substring(delim + 1);
contentType = contentType.substring(0, delim);
// Double-check in case the attachment was created by an old
// I2P-Bote version that didn't detect MIME types correctly.
if ("application/octet-stream".equals(contentType)) {
// Find the filename
String filename = "";
delim = params.indexOf("name=");
if (delim >= 0) {
filename = params.substring(delim + 5);
delim = filename.indexOf(' ');
if (delim >= 0)
filename = params.substring(0, delim);
}
if (!filename.isEmpty()) {
MimetypesFileTypeMap mimeTypeMap = new MimetypesFileTypeMap();
contentType = mimeTypeMap.getContentType(filename);
}
}
}
return contentType;
}
} catch (PasswordException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (MessagingException e) {
e.printStackTrace();
}
}
return null;
}
@Override
public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
if (sUriMatcher.match(uri) == UriMatcher.NO_MATCH)
throw new FileNotFoundException("Invalid URI: " + uri);
if (!"r".equals(mode))
throw new FileNotFoundException("Attachments can only be read");
ParcelFileDescriptor[] pipe;
try {
pipe = ParcelFileDescriptor.createPipe();
} catch (IOException e) {
Log.e(getClass().getSimpleName(), "Exception opening pipe", e);
throw new FileNotFoundException("Could not open pipe for: "
+ uri.toString());
}
try {
Part attachment = getAttachment(uri);
if (attachment == null)
throw new FileNotFoundException("Unknown email or attachment for URI " + uri);
new TransferThread(attachment.getInputStream(),
new ParcelFileDescriptor.AutoCloseOutputStream(pipe[1])).start();
} catch (Exception e) {
Log.e(getClass().getSimpleName(), "Exception accessing attachment", e);
throw new FileNotFoundException("Exception accessing attachment: " + e.getLocalizedMessage());
}
return pipe[0];
}
static class TransferThread extends Thread {
InputStream in;
OutputStream out;
TransferThread(InputStream in, OutputStream out) {
this.in = in;
this.out = out;
}
@Override
public void run() {
byte[] buf = new byte[8192];
int len;
try {
while ((len = in.read(buf)) > 0) {
out.write(buf, 0, len);
}
in.close();
out.flush();
out.close();
} catch (IOException e) {
Log.e(getClass().getSimpleName(), "Exception transferring file", e);
}
}
}
@Override
public Uri insert(Uri uri, ContentValues values) {
return null;
}
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
return 0;
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
return 0;
}
private Part getAttachment(Uri uri) throws PasswordException, IOException, MessagingException {
List<String> segments = uri.getPathSegments();
String folderName = segments.get(0);
String messageId = segments.get(1);
int partNum = Integer.valueOf(segments.get(2));
Email email = BoteHelper.getEmail(folderName, messageId);
if (email != null) {
if (partNum >= 0 && partNum < email.getParts().size())
return email.getParts().get(partNum);
}
return null;
}
}