/*******************************************************************************
* Copyright (C) 2015 Connor Lanigan (email: dev@connorlanigan.com)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*******************************************************************************/
package de.norvos.messages;
import java.util.concurrent.BlockingQueue;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.whispersystems.libaxolotl.DuplicateMessageException;
import org.whispersystems.libaxolotl.InvalidKeyException;
import org.whispersystems.libaxolotl.InvalidKeyIdException;
import org.whispersystems.libaxolotl.InvalidMessageException;
import org.whispersystems.libaxolotl.InvalidVersionException;
import org.whispersystems.libaxolotl.LegacyMessageException;
import org.whispersystems.libaxolotl.NoSessionException;
import org.whispersystems.libaxolotl.UntrustedIdentityException;
import org.whispersystems.textsecure.api.crypto.TextSecureCipher;
import org.whispersystems.textsecure.api.messages.TextSecureContent;
import org.whispersystems.textsecure.api.messages.TextSecureDataMessage;
import org.whispersystems.textsecure.api.messages.TextSecureEnvelope;
import org.whispersystems.textsecure.api.messages.multidevice.TextSecureSyncMessage;
import de.norvos.axolotl.AxolotlStore;
import de.norvos.eventbus.EventBus;
import de.norvos.eventbus.events.MessageReceivedEvent;
import de.norvos.utils.Constants;
import javafx.concurrent.Task;
/**
* Provides methods for decrypting received messages.
*
* @author Connor Lanigan
*/
public class MessageDecrypter {
private static final Logger LOGGER = LoggerFactory.getLogger(MessageDecrypter.class);
private static BlockingQueue<TextSecureEnvelope> queue;
private static Task<Void> task;
private static Thread thread;
private static void handleEndSessionMessage(final TextSecureEnvelope envelope,
final TextSecureDataMessage message) {
LOGGER.warn("End-session-message received. Will be ignored.");
}
private static void handleGroupMessage(final TextSecureEnvelope envelope, final TextSecureDataMessage message) {
LOGGER.warn("Group message received. Will be ignored.");
}
protected static void handleInvalidMessage(final TextSecureEnvelope envelope) {
LOGGER.warn("Invalid message received. Will be ignored.");
}
private static void handleMediaMessage(final TextSecureEnvelope envelope, final TextSecureDataMessage message) {
LOGGER.warn("Media message received. Will be ignored.");
}
private static void handleSyncMessage(final TextSecureSyncMessage syncMessage) {
LOGGER.warn("Synchronization message received. Will be ignored.");
}
private static void handleTextMessage(final TextSecureEnvelope envelope, final TextSecureDataMessage message) {
// TODO save attachments
final DecryptedMessage decryptedMessage = new DecryptedMessage(System.currentTimeMillis(), false,
message.getBody().get(), envelope.getSource(), "", false, Constants.NO_ATTACHMENT_ID,
Constants.NO_MESSAGE_ID);
MessageService.getInstance().storeMessage(decryptedMessage);
EventBus.sendEvent(new MessageReceivedEvent(decryptedMessage));
}
protected static void handleUntrustedMessage(final TextSecureEnvelope envelope) {
LOGGER.warn("Untrusted message received. Will be ignored.");
}
/**
* Adds an encrypted message to the queue. It will be automatically
* decrypted.
*
* @param envelope
* the encrypted message envelope
*/
public static void pushEncryptedMessage(final TextSecureEnvelope envelope) {
while (true) {
try {
queue.put(envelope);
return;
} catch (final InterruptedException e) {
}
}
}
/**
* Starts the Decrypter. It will automatically decrypt messages from the
* queue and fire a {@link MessageReceivedEvent} after decrypting each one.
*/
synchronized public static void start() {
if (thread != null && thread.isAlive()) {
throw new IllegalStateException("MessageDecrypter is already running!");
}
task = new Task<Void>() {
@Override
protected Void call() {
while (!isCancelled()) {
TextSecureEnvelope envelope = null;
try {
envelope = queue.take();
final TextSecureCipher cipher = new TextSecureCipher(envelope.getSourceAddress(),
AxolotlStore.getInstance());
final TextSecureContent content = cipher.decrypt(envelope);
if (content.getDataMessage().isPresent()) {
final TextSecureDataMessage message = content.getDataMessage().get();
if (message.isEndSession()) {
handleEndSessionMessage(envelope, message);
} else if (message.isGroupUpdate()) {
handleGroupMessage(envelope, message);
} else if (message.getAttachments().isPresent()) {
handleMediaMessage(envelope, message);
} else {
handleTextMessage(envelope, message);
}
} else {
handleSyncMessage(content.getSyncMessage().get());
}
} catch (final InterruptedException e) {
continue;
} catch (InvalidVersionException | InvalidMessageException | InvalidKeyException
| DuplicateMessageException | InvalidKeyIdException | NoSessionException
| LegacyMessageException e) {
handleInvalidMessage(envelope);
} catch (final UntrustedIdentityException e) {
handleUntrustedMessage(envelope);
}
}
return null;
}
};
thread = new Thread(task);
thread.start();
}
/**
* Stops the Decrypter.
*/
synchronized public static void stop() {
if (task != null) {
task.cancel();
}
}
}