/** * Copyright (C) 2011 Whisper Systems * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.thoughtcrime.securesms.crypto; import android.content.Context; import android.database.Cursor; import android.preference.PreferenceManager; import android.util.Log; import org.thoughtcrime.securesms.ApplicationPreferencesActivity; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.EncryptingSmsDatabase; import org.thoughtcrime.securesms.database.MmsDatabase; import org.thoughtcrime.securesms.database.SmsDatabase; import org.thoughtcrime.securesms.database.model.SmsMessageRecord; import org.thoughtcrime.securesms.mms.TextTransport; import org.thoughtcrime.securesms.notifications.MessageNotifier; import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.RecipientFactory; import org.thoughtcrime.securesms.recipients.RecipientFormattingException; import org.thoughtcrime.securesms.recipients.Recipients; import org.thoughtcrime.securesms.sms.SmsTransportDetails; import org.thoughtcrime.securesms.util.Hex; import org.thoughtcrime.securesms.util.WorkerThread; import java.io.IOException; import java.util.LinkedList; import java.util.List; import ws.com.google.android.mms.ContentType; import ws.com.google.android.mms.MmsException; import ws.com.google.android.mms.pdu.MultimediaMessagePdu; import ws.com.google.android.mms.pdu.PduParser; import ws.com.google.android.mms.pdu.RetrieveConf; import ws.com.google.android.mms.pdu.SendReq; /** * A work queue for processing a number of encryption operations. * * @author Moxie Marlinspike */ public class DecryptingQueue { private static final List workQueue = new LinkedList(); static { Thread workerThread = new WorkerThread(workQueue, "Async Decryption Thread"); workerThread.start(); } public static void scheduleDecryption(Context context, MasterSecret masterSecret, long messageId, long threadId, MultimediaMessagePdu mms) { MmsDecryptionItem runnable = new MmsDecryptionItem(context, masterSecret, messageId, threadId, mms); synchronized (workQueue) { workQueue.add(runnable); workQueue.notifyAll(); } } public static void scheduleDecryption(Context context, MasterSecret masterSecret, long messageId, long threadId, String originator, String body, boolean isSecureMessage, boolean isKeyExchange) { DecryptionWorkItem runnable = new DecryptionWorkItem(context, masterSecret, messageId, threadId, originator, body, isSecureMessage, isKeyExchange); synchronized (workQueue) { workQueue.add(runnable); workQueue.notifyAll(); } } public static void schedulePendingDecrypts(Context context, MasterSecret masterSecret) { Log.w("DecryptingQueue", "Processing pending decrypts..."); EncryptingSmsDatabase.Reader reader = null; SmsMessageRecord record; try { reader = DatabaseFactory.getEncryptingSmsDatabase(context).getDecryptInProgressMessages(masterSecret); while ((record = reader.getNext()) != null) { scheduleDecryptFromCursor(context, masterSecret, record); } } finally { if (reader != null) reader.close(); } } public static void scheduleRogueMessages(Context context, MasterSecret masterSecret, Recipient recipient) { SmsDatabase.Reader reader = null; SmsMessageRecord record; try { Cursor cursor = DatabaseFactory.getSmsDatabase(context).getEncryptedRogueMessages(recipient); reader = DatabaseFactory.getEncryptingSmsDatabase(context).readerFor(masterSecret, cursor); while ((record = reader.getNext()) != null) { DatabaseFactory.getSmsDatabase(context).markAsDecrypting(record.getId()); scheduleDecryptFromCursor(context, masterSecret, record); } } finally { if (reader != null) reader.close(); } } private static void scheduleDecryptFromCursor(Context context, MasterSecret masterSecret, SmsMessageRecord record) { long messageId = record.getId(); long threadId = record.getThreadId(); String body = record.getBody().getBody(); String originator = record.getIndividualRecipient().getNumber(); boolean isSecureMessage = record.isSecure(); boolean isKeyExchange = record.isKeyExchange(); scheduleDecryption(context, masterSecret, messageId, threadId, originator, body, isSecureMessage, isKeyExchange); } private static class MmsDecryptionItem implements Runnable { private long messageId; private long threadId; private Context context; private MasterSecret masterSecret; private MultimediaMessagePdu pdu; public MmsDecryptionItem(Context context, MasterSecret masterSecret, long messageId, long threadId, MultimediaMessagePdu pdu) { this.context = context; this.masterSecret = masterSecret; this.messageId = messageId; this.threadId = threadId; this.pdu = pdu; } private byte[] getEncryptedData() { for (int i=0;i