/** * 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.service; import android.content.Context; import android.content.Intent; import android.preference.PreferenceManager; import android.util.Log; import android.util.Pair; import org.thoughtcrime.securesms.ApplicationPreferencesActivity; import org.thoughtcrime.securesms.crypto.DecryptingQueue; import org.thoughtcrime.securesms.crypto.InvalidKeyException; import org.thoughtcrime.securesms.crypto.InvalidVersionException; import org.thoughtcrime.securesms.crypto.KeyExchangeMessage; import org.thoughtcrime.securesms.crypto.KeyExchangeProcessor; import org.thoughtcrime.securesms.crypto.MasterSecret; import org.thoughtcrime.securesms.crypto.MasterSecretUtil; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.EncryptingSmsDatabase; import org.thoughtcrime.securesms.database.SmsDatabase; import org.thoughtcrime.securesms.notifications.MessageNotifier; import org.thoughtcrime.securesms.protocol.WirePrefix; import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.sms.IncomingKeyExchangeMessage; import org.thoughtcrime.securesms.sms.IncomingTextMessage; import org.thoughtcrime.securesms.sms.MultipartSmsMessageHandler; import java.util.List; public class SmsReceiver { private MultipartSmsMessageHandler multipartMessageHandler = new MultipartSmsMessageHandler(); private final Context context; public SmsReceiver(Context context) { this.context = context; } private IncomingTextMessage assembleMessageFragments(List messages) { IncomingTextMessage message = new IncomingTextMessage(messages); if (WirePrefix.isEncryptedMessage(message.getMessageBody()) || WirePrefix.isKeyExchange(message.getMessageBody())) { return multipartMessageHandler.processPotentialMultipartMessage(message); } else { return message; } } private Pair storeSecureMessage(MasterSecret masterSecret, IncomingTextMessage message) { Pair messageAndThreadId = DatabaseFactory.getEncryptingSmsDatabase(context) .insertMessageInbox(masterSecret, message); if (masterSecret != null) { DecryptingQueue.scheduleDecryption(context, masterSecret, messageAndThreadId.first, message.getSender(), message.getMessageBody(), message.isSecureMessage()); } return messageAndThreadId; } private Pair storeStandardMessage(MasterSecret masterSecret, IncomingTextMessage message) { EncryptingSmsDatabase encryptingDatabase = DatabaseFactory.getEncryptingSmsDatabase(context); SmsDatabase plaintextDatabase = DatabaseFactory.getSmsDatabase(context); if (masterSecret != null) { return encryptingDatabase.insertMessageInbox(masterSecret, message); } else if (MasterSecretUtil.hasAsymmericMasterSecret(context)) { return encryptingDatabase.insertMessageInbox(MasterSecretUtil.getAsymmetricMasterSecret(context, null), message); } else { return plaintextDatabase.insertMessageInbox(message); } } private Pair storeKeyExchangeMessage(MasterSecret masterSecret, IncomingKeyExchangeMessage message) { if (PreferenceManager.getDefaultSharedPreferences(context).getBoolean(ApplicationPreferencesActivity.AUTO_KEY_EXCHANGE_PREF, true)) { try { Recipient recipient = new Recipient(null, message.getSender(), null, null); KeyExchangeMessage keyExchangeMessage = new KeyExchangeMessage(message.getMessageBody()); KeyExchangeProcessor processor = new KeyExchangeProcessor(context, masterSecret, recipient); Log.w("SmsReceiver", "Received key with fingerprint: " + keyExchangeMessage.getPublicKey().getFingerprint()); if (processor.isStale(keyExchangeMessage)) { message.setStale(true); } else if (!processor.hasCompletedSession() || processor.hasSameSessionIdentity(keyExchangeMessage)) { message.setProcessed(true); Pair messageAndThreadId = storeStandardMessage(masterSecret, message); processor.processKeyExchangeMessage(keyExchangeMessage, messageAndThreadId.second); return messageAndThreadId; } } catch (InvalidVersionException e) { Log.w("SmsReceiver", e); } catch (InvalidKeyException e) { Log.w("SmsReceiver", e); } } return storeStandardMessage(masterSecret, message); } private Pair storeMessage(MasterSecret masterSecret, IncomingTextMessage message) { if (message.isSecureMessage()) return storeSecureMessage(masterSecret, message); else if (message.isKeyExchange()) return storeKeyExchangeMessage(masterSecret, (IncomingKeyExchangeMessage)message); else return storeStandardMessage(masterSecret, message); } private void handleReceiveMessage(MasterSecret masterSecret, Intent intent) { List messagesList = intent.getExtras().getParcelableArrayList("text_messages"); IncomingTextMessage message = assembleMessageFragments(messagesList); if (message != null) { Pair messageAndThreadId = storeMessage(masterSecret, message); MessageNotifier.updateNotification(context, masterSecret, messageAndThreadId.second); } } public void process(MasterSecret masterSecret, Intent intent) { if (intent.getAction().equals(SendReceiveService.RECEIVE_SMS_ACTION)) { handleReceiveMessage(masterSecret, intent); } } }