public boolean createSessionsIfNeeded(final Conversation conversation) { Log.i( Config.LOGTAG, AxolotlService.getLogprefix(account) + "Creating axolotl sessions if needed..."); boolean newSessions = false; Set<AxolotlAddress> addresses = findDevicesWithoutSession(conversation); for (AxolotlAddress address : addresses) { Log.d( Config.LOGTAG, AxolotlService.getLogprefix(account) + "Processing device: " + address.toString()); FetchStatus status = fetchStatusMap.get(address); if (status == null || status == FetchStatus.ERROR) { fetchStatusMap.put(address, FetchStatus.PENDING); this.buildSessionFromPEP(address); newSessions = true; } else if (status == FetchStatus.PENDING) { newSessions = true; } else { Log.d( Config.LOGTAG, AxolotlService.getLogprefix(account) + "Already fetching bundle for " + address.toString()); } } return newSessions; }
@Nullable private XmppAxolotlMessage buildHeader(Contact contact) { final XmppAxolotlMessage axolotlMessage = new XmppAxolotlMessage(contact.getJid().toBareJid(), getOwnDeviceId()); Set<XmppAxolotlSession> contactSessions = findSessionsforContact(contact); Set<XmppAxolotlSession> ownSessions = findOwnSessions(); if (contactSessions.isEmpty()) { return null; } Log.d( Config.LOGTAG, AxolotlService.getLogprefix(account) + "Building axolotl foreign keyElements..."); for (XmppAxolotlSession session : contactSessions) { Log.v( Config.LOGTAG, AxolotlService.getLogprefix(account) + session.getRemoteAddress().toString()); axolotlMessage.addDevice(session); } Log.d( Config.LOGTAG, AxolotlService.getLogprefix(account) + "Building axolotl own keyElements..."); for (XmppAxolotlSession session : ownSessions) { Log.v( Config.LOGTAG, AxolotlService.getLogprefix(account) + session.getRemoteAddress().toString()); axolotlMessage.addDevice(session); } return axolotlMessage; }
public int getNextEncryption() { final AxolotlService axolotlService = getAccount().getAxolotlService(); int next = this.getIntAttribute(ATTRIBUTE_NEXT_ENCRYPTION, -1); if (next == -1) { if (Config.X509_VERIFICATION && mode == MODE_SINGLE) { if (axolotlService != null && axolotlService.isContactAxolotlCapable(getContact())) { return Message.ENCRYPTION_AXOLOTL; } else { return Message.ENCRYPTION_NONE; } } int outgoing = this.getMostRecentlyUsedOutgoingEncryption(); if (outgoing == Message.ENCRYPTION_NONE) { next = this.getMostRecentlyUsedIncomingEncryption(); } else { next = outgoing; } } if (Config.FORCE_E2E_ENCRYPTION && mode == MODE_SINGLE && next <= 0) { if (axolotlService != null && axolotlService.isContactAxolotlCapable(getContact())) { return Message.ENCRYPTION_AXOLOTL; } else { return Message.ENCRYPTION_OTR; } } return next; }
public XmppAxolotlMessage fetchAxolotlMessageFromCache(Message message) { XmppAxolotlMessage axolotlMessage = messageCache.get(message.getUuid()); if (axolotlMessage != null) { Log.d( Config.LOGTAG, AxolotlService.getLogprefix(account) + "Cache hit: " + message.getUuid()); messageCache.remove(message.getUuid()); } else { Log.d( Config.LOGTAG, AxolotlService.getLogprefix(account) + "Cache miss: " + message.getUuid()); } return axolotlMessage; }
private void parseEvent(final Element event, final Jid from, final Account account) { Element items = event.findChild("items"); String node = items == null ? null : items.getAttribute("node"); if ("urn:xmpp:avatar:metadata".equals(node)) { Avatar avatar = Avatar.parseMetadata(items); if (avatar != null) { avatar.owner = from.toBareJid(); if (mXmppConnectionService.getFileBackend().isAvatarCached(avatar)) { if (account.getJid().toBareJid().equals(from)) { if (account.setAvatar(avatar.getFilename())) { mXmppConnectionService.databaseBackend.updateAccount(account); } mXmppConnectionService.getAvatarService().clear(account); mXmppConnectionService.updateConversationUi(); mXmppConnectionService.updateAccountUi(); } else { Contact contact = account.getRoster().getContact(from); contact.setAvatar(avatar); mXmppConnectionService.getAvatarService().clear(contact); mXmppConnectionService.updateConversationUi(); mXmppConnectionService.updateRosterUi(); } } else { mXmppConnectionService.fetchAvatar(account, avatar); } } } else if ("http://jabber.org/protocol/nick".equals(node)) { Element i = items.findChild("item"); Element nick = i == null ? null : i.findChild("nick", "http://jabber.org/protocol/nick"); if (nick != null && nick.getContent() != null) { Contact contact = account.getRoster().getContact(from); contact.setPresenceName(nick.getContent()); mXmppConnectionService.getAvatarService().clear(account); mXmppConnectionService.updateConversationUi(); mXmppConnectionService.updateAccountUi(); } } else if (AxolotlService.PEP_DEVICE_LIST.equals(node)) { Log.d( Config.LOGTAG, AxolotlService.getLogprefix(account) + "Received PEP device list update from " + from + ", processing..."); Element item = items.findChild("item"); Set<Integer> deviceIds = mXmppConnectionService.getIqParser().deviceIds(item); AxolotlService axolotlService = account.getAxolotlService(); axolotlService.registerDevices(from, deviceIds); mXmppConnectionService.updateAccountUi(); } }
private Message parseAxolotlChat( Element axolotlMessage, Jid from, String id, Conversation conversation, int status) { Message finishedMessage = null; AxolotlService service = conversation.getAccount().getAxolotlService(); XmppAxolotlMessage xmppAxolotlMessage = XmppAxolotlMessage.fromElement(axolotlMessage, from.toBareJid()); XmppAxolotlMessage.XmppAxolotlPlaintextMessage plaintextMessage = service.processReceivingPayloadMessage(xmppAxolotlMessage); if (plaintextMessage != null) { finishedMessage = new Message( conversation, plaintextMessage.getPlaintext(), Message.ENCRYPTION_AXOLOTL, status); finishedMessage.setAxolotlFingerprint(plaintextMessage.getFingerprint()); Log.d( Config.LOGTAG, AxolotlService.getLogprefix(finishedMessage.getConversation().getAccount()) + " Received Message with session fingerprint: " + plaintextMessage.getFingerprint()); } return finishedMessage; }
public void wipeOtherPepDevices() { Set<Integer> deviceIds = new HashSet<>(); deviceIds.add(getOwnDeviceId()); IqPacket publish = mXmppConnectionService.getIqGenerator().publishDeviceIds(deviceIds); Log.d( Config.LOGTAG, AxolotlService.getLogprefix(account) + "Wiping all other devices from Pep:" + publish); mXmppConnectionService.sendIqPacket( account, publish, new OnIqPacketReceived() { @Override public void onIqPacketReceived(Account account, IqPacket packet) { // TODO: implement this! } }); }
private XmppAxolotlSession getReceivingSession(XmppAxolotlMessage message) { AxolotlAddress senderAddress = new AxolotlAddress(message.getFrom().toString(), message.getSenderDeviceId()); XmppAxolotlSession session = sessions.get(senderAddress); if (session == null) { Log.d( Config.LOGTAG, AxolotlService.getLogprefix(account) + "Account: " + account.getJid() + " No axolotl session found while parsing received message " + message); session = recreateUncachedSession(senderAddress); if (session == null) { session = new XmppAxolotlSession(account, axolotlStore, senderAddress); } } return session; }
private void putDevicesForJid( String bareJid, List<Integer> deviceIds, SQLiteAxolotlStore store) { for (Integer deviceId : deviceIds) { AxolotlAddress axolotlAddress = new AxolotlAddress(bareJid, deviceId); Log.d( Config.LOGTAG, AxolotlService.getLogprefix(account) + "Building session for remote address: " + axolotlAddress.toString()); String fingerprint = store .loadSession(axolotlAddress) .getSessionState() .getRemoteIdentityKey() .getFingerprint() .replaceAll("\\s", ""); this.put( axolotlAddress, new XmppAxolotlSession(account, store, axolotlAddress, fingerprint)); } }
public Set<AxolotlAddress> findDevicesWithoutSession(final Conversation conversation) { Log.d( Config.LOGTAG, AxolotlService.getLogprefix(account) + "Finding devices without session for " + conversation.getContact().getJid().toBareJid()); Jid contactJid = conversation.getContact().getJid().toBareJid(); Set<AxolotlAddress> addresses = new HashSet<>(); if (deviceIds.get(contactJid) != null) { for (Integer foreignId : this.deviceIds.get(contactJid)) { AxolotlAddress address = new AxolotlAddress(contactJid.toString(), foreignId); if (sessions.get(address) == null) { IdentityKey identityKey = axolotlStore.loadSession(address).getSessionState().getRemoteIdentityKey(); if (identityKey != null) { Log.d( Config.LOGTAG, AxolotlService.getLogprefix(account) + "Already have session for " + address.toString() + ", adding to cache..."); XmppAxolotlSession session = new XmppAxolotlSession( account, axolotlStore, address, identityKey.getFingerprint().replaceAll("\\s", "")); sessions.put(address, session); } else { Log.d( Config.LOGTAG, AxolotlService.getLogprefix(account) + "Found device " + account.getJid().toBareJid() + ":" + foreignId); addresses.add(new AxolotlAddress(contactJid.toString(), foreignId)); } } } } else { Log.w(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Have no target devices in PEP!"); } if (deviceIds.get(account.getJid().toBareJid()) != null) { for (Integer ownId : this.deviceIds.get(account.getJid().toBareJid())) { AxolotlAddress address = new AxolotlAddress(account.getJid().toBareJid().toString(), ownId); if (sessions.get(address) == null) { IdentityKey identityKey = axolotlStore.loadSession(address).getSessionState().getRemoteIdentityKey(); if (identityKey != null) { Log.d( Config.LOGTAG, AxolotlService.getLogprefix(account) + "Already have session for " + address.toString() + ", adding to cache..."); XmppAxolotlSession session = new XmppAxolotlSession( account, axolotlStore, address, identityKey.getFingerprint().replaceAll("\\s", "")); sessions.put(address, session); } else { Log.d( Config.LOGTAG, AxolotlService.getLogprefix(account) + "Found device " + account.getJid().toBareJid() + ":" + ownId); addresses.add(new AxolotlAddress(account.getJid().toBareJid().toString(), ownId)); } } } } return addresses; }
private void buildSessionFromPEP(final AxolotlAddress address) { Log.i( Config.LOGTAG, AxolotlService.getLogprefix(account) + "Building new sesstion for " + address.getDeviceId()); try { IqPacket bundlesPacket = mXmppConnectionService .getIqGenerator() .retrieveBundlesForDevice(Jid.fromString(address.getName()), address.getDeviceId()); Log.d( Config.LOGTAG, AxolotlService.getLogprefix(account) + "Retrieving bundle: " + bundlesPacket); mXmppConnectionService.sendIqPacket( account, bundlesPacket, new OnIqPacketReceived() { private void finish() { AxolotlAddress ownAddress = new AxolotlAddress(account.getJid().toBareJid().toString(), 0); if (!fetchStatusMap.getAll(ownAddress).containsValue(FetchStatus.PENDING) && !fetchStatusMap.getAll(address).containsValue(FetchStatus.PENDING)) { mXmppConnectionService.keyStatusUpdated(); } } @Override public void onIqPacketReceived(Account account, IqPacket packet) { Log.d( Config.LOGTAG, AxolotlService.getLogprefix(account) + "Received preKey IQ packet, processing..."); final IqParser parser = mXmppConnectionService.getIqParser(); final List<PreKeyBundle> preKeyBundleList = parser.preKeys(packet); final PreKeyBundle bundle = parser.bundle(packet); if (preKeyBundleList.isEmpty() || bundle == null) { Log.e( Config.LOGTAG, AxolotlService.getLogprefix(account) + "preKey IQ packet invalid: " + packet); fetchStatusMap.put(address, FetchStatus.ERROR); finish(); return; } Random random = new Random(); final PreKeyBundle preKey = preKeyBundleList.get(random.nextInt(preKeyBundleList.size())); if (preKey == null) { // should never happen fetchStatusMap.put(address, FetchStatus.ERROR); finish(); return; } final PreKeyBundle preKeyBundle = new PreKeyBundle( 0, address.getDeviceId(), preKey.getPreKeyId(), preKey.getPreKey(), bundle.getSignedPreKeyId(), bundle.getSignedPreKey(), bundle.getSignedPreKeySignature(), bundle.getIdentityKey()); axolotlStore.saveIdentity(address.getName(), bundle.getIdentityKey()); try { SessionBuilder builder = new SessionBuilder(axolotlStore, address); builder.process(preKeyBundle); XmppAxolotlSession session = new XmppAxolotlSession( account, axolotlStore, address, bundle.getIdentityKey().getFingerprint().replaceAll("\\s", "")); sessions.put(address, session); fetchStatusMap.put(address, FetchStatus.SUCCESS); } catch (UntrustedIdentityException | InvalidKeyException e) { Log.e( Config.LOGTAG, AxolotlService.getLogprefix(account) + "Error building session for " + address + ": " + e.getClass().getName() + ", " + e.getMessage()); fetchStatusMap.put(address, FetchStatus.ERROR); } finish(); } }); } catch (InvalidJidException e) { Log.e( Config.LOGTAG, AxolotlService.getLogprefix(account) + "Got address with invalid jid: " + address.getName()); } }