private void handleUntrustedIdentityMessage( MasterSecret masterSecret, TextSecureEnvelope envelope, Optional<Long> smsMessageId) { try { EncryptingSmsDatabase database = DatabaseFactory.getEncryptingSmsDatabase(context); Recipients recipients = RecipientFactory.getRecipientsFromString(context, envelope.getSource(), false); long recipientId = recipients.getPrimaryRecipient().getRecipientId(); PreKeyWhisperMessage whisperMessage = new PreKeyWhisperMessage(envelope.getMessage()); IdentityKey identityKey = whisperMessage.getIdentityKey(); String encoded = Base64.encodeBytes(envelope.getMessage()); IncomingTextMessage textMessage = new IncomingTextMessage( envelope.getSource(), envelope.getSourceDevice(), envelope.getTimestamp(), encoded, Optional.<TextSecureGroup>absent()); if (!smsMessageId.isPresent()) { IncomingPreKeyBundleMessage bundleMessage = new IncomingPreKeyBundleMessage(textMessage, encoded); Pair<Long, Long> messageAndThreadId = database.insertMessageInbox(masterSecret, bundleMessage); database.setMismatchedIdentity(messageAndThreadId.first, recipientId, identityKey); MessageNotifier.updateNotification(context, masterSecret, messageAndThreadId.second); } else { database.updateMessageBody(masterSecret, smsMessageId.get(), encoded); database.markAsPreKeyBundle(smsMessageId.get()); database.setMismatchedIdentity(smsMessageId.get(), recipientId, identityKey); } } catch (InvalidMessageException | InvalidVersionException e) { throw new AssertionError(e); } }
private void handleEndSessionMessage( MasterSecret masterSecret, TextSecureEnvelope envelope, TextSecureMessage message, Optional<Long> smsMessageId) { EncryptingSmsDatabase smsDatabase = DatabaseFactory.getEncryptingSmsDatabase(context); IncomingTextMessage incomingTextMessage = new IncomingTextMessage( envelope.getSource(), envelope.getSourceDevice(), message.getTimestamp(), "", Optional.<TextSecureGroup>absent()); long threadId; if (!smsMessageId.isPresent()) { IncomingEndSessionMessage incomingEndSessionMessage = new IncomingEndSessionMessage(incomingTextMessage); Pair<Long, Long> messageAndThreadId = smsDatabase.insertMessageInbox(masterSecret, incomingEndSessionMessage); threadId = messageAndThreadId.second; } else { smsDatabase.markAsEndSession(smsMessageId.get()); threadId = smsDatabase.getThreadIdForMessage(smsMessageId.get()); } SessionStore sessionStore = new TextSecureSessionStore(context, masterSecret); sessionStore.deleteAllSessions(envelope.getSource()); SecurityEvent.broadcastSecurityUpdateEvent(context, threadId); MessageNotifier.updateNotification(context, masterSecret, threadId); }
private void initializeSessionsV3(SessionState aliceSessionState, SessionState bobSessionState) throws InvalidKeyException { ECKeyPair aliceIdentityKeyPair = Curve.generateKeyPair(); IdentityKeyPair aliceIdentityKey = new IdentityKeyPair( new IdentityKey(aliceIdentityKeyPair.getPublicKey()), aliceIdentityKeyPair.getPrivateKey()); ECKeyPair aliceBaseKey = Curve.generateKeyPair(); ECKeyPair aliceEphemeralKey = Curve.generateKeyPair(); ECKeyPair alicePreKey = aliceBaseKey; ECKeyPair bobIdentityKeyPair = Curve.generateKeyPair(); IdentityKeyPair bobIdentityKey = new IdentityKeyPair( new IdentityKey(bobIdentityKeyPair.getPublicKey()), bobIdentityKeyPair.getPrivateKey()); ECKeyPair bobBaseKey = Curve.generateKeyPair(); ECKeyPair bobEphemeralKey = bobBaseKey; ECKeyPair bobPreKey = Curve.generateKeyPair(); AliceAxolotlParameters aliceParameters = AliceAxolotlParameters.newBuilder() .setOurBaseKey(aliceBaseKey) .setOurIdentityKey(aliceIdentityKey) .setTheirOneTimePreKey(Optional.<ECPublicKey>absent()) .setTheirRatchetKey(bobEphemeralKey.getPublicKey()) .setTheirSignedPreKey(bobBaseKey.getPublicKey()) .setTheirIdentityKey(bobIdentityKey.getPublicKey()) .create(); BobAxolotlParameters bobParameters = BobAxolotlParameters.newBuilder() .setOurRatchetKey(bobEphemeralKey) .setOurSignedPreKey(bobBaseKey) .setOurOneTimePreKey(Optional.<ECKeyPair>absent()) .setOurIdentityKey(bobIdentityKey) .setTheirIdentityKey(aliceIdentityKey.getPublicKey()) .setTheirBaseKey(aliceBaseKey.getPublicKey()) .create(); RatchetingSession.initializeSession(aliceSessionState, 3, aliceParameters); RatchetingSession.initializeSession(bobSessionState, 3, bobParameters); }
@Override public void onRun(MasterSecret masterSecret) throws NoSuchMessageException { PushDatabase database = DatabaseFactory.getPushDatabase(context); TextSecureEnvelope envelope = database.get(messageId); Optional<Long> optionalSmsMessageId = smsMessageId > 0 ? Optional.of(smsMessageId) : Optional.<Long>absent(); handleMessage(masterSecret, envelope, optionalSmsMessageId); database.delete(messageId); }
private Optional<IncomingTextMessage> assembleMessageFragments( Object[] pdus, int subscriptionId) { List<IncomingTextMessage> messages = new LinkedList<>(); for (Object pdu : pdus) { messages.add(new IncomingTextMessage(SmsMessage.createFromPdu((byte[]) pdu), subscriptionId)); } if (messages.isEmpty()) { return Optional.absent(); } return Optional.of(new IncomingTextMessage(messages)); }
private Pair<Long, Long> insertPlaceholder( MasterSecret masterSecret, TextSecureEnvelope envelope) { EncryptingSmsDatabase database = DatabaseFactory.getEncryptingSmsDatabase(context); IncomingTextMessage textMessage = new IncomingTextMessage( envelope.getSource(), envelope.getSourceDevice(), envelope.getTimestamp(), "", Optional.<TextSecureGroup>absent()); textMessage = new IncomingEncryptedMessage(textMessage, ""); return database.insertMessageInbox(masterSecret, textMessage); }
public Optional<NotificationInd> getNotification(long messageId) { Cursor cursor = null; try { cursor = rawQuery(RAW_ID_WHERE, new String[] {String.valueOf(messageId)}); if (cursor != null && cursor.moveToNext()) { PduHeaders headers = new PduHeaders(); PduHeadersBuilder builder = new PduHeadersBuilder(headers, cursor); builder.addText(CONTENT_LOCATION, PduHeaders.CONTENT_LOCATION); builder.addLong(NORMALIZED_DATE_SENT, PduHeaders.DATE); builder.addLong(EXPIRY, PduHeaders.EXPIRY); builder.addLong(MESSAGE_SIZE, PduHeaders.MESSAGE_SIZE); builder.addText(TRANSACTION_ID, PduHeaders.TRANSACTION_ID); return Optional.of(new NotificationInd(headers)); } else { return Optional.absent(); } } finally { if (cursor != null) cursor.close(); } }
public class ThumbnailView extends FrameLayout { private static final String TAG = ThumbnailView.class.getSimpleName(); private boolean hideControls; private ImageView image; private ImageView removeButton; private int backgroundColorHint; private int radius; private OnClickListener parentClickListener; private Optional<TransferControlView> transferControls = Optional.absent(); private ListenableFutureTask<SlideDeck> slideDeckFuture = null; private SlideDeckListener slideDeckListener = null; private ThumbnailClickListener thumbnailClickListener = null; private ThumbnailClickListener downloadClickListener = null; private String slideId = null; private Slide slide = null; public ThumbnailView(Context context) { this(context, null); } public ThumbnailView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public ThumbnailView(final Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); inflate(context, R.layout.thumbnail_view, this); radius = getResources().getDimensionPixelSize(R.dimen.message_bubble_corner_radius); image = (ImageView) findViewById(R.id.thumbnail_image); super.setOnClickListener(new ThumbnailClickDispatcher()); if (attrs != null) { TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.ThumbnailView, 0, 0); backgroundColorHint = typedArray.getColor(0, Color.BLACK); typedArray.recycle(); } } @Override public void setOnClickListener(OnClickListener l) { parentClickListener = l; } @Override public void setFocusable(boolean focusable) { super.setFocusable(focusable); if (transferControls.isPresent()) transferControls.get().setFocusable(focusable); } @Override public void setClickable(boolean clickable) { super.setClickable(clickable); if (transferControls.isPresent()) transferControls.get().setClickable(clickable); } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); if (removeButton != null) { final int paddingHorizontal = removeButton.getWidth() / 2; final int paddingVertical = removeButton.getHeight() / 2; image.setPadding(paddingHorizontal, paddingVertical, paddingHorizontal, 0); } } private ImageView getRemoveButton() { if (removeButton == null) removeButton = ViewUtil.inflateStub(this, R.id.remove_button_stub); return removeButton; } private TransferControlView getTransferControls() { if (!transferControls.isPresent()) { transferControls = Optional.of( (TransferControlView) ViewUtil.inflateStub(this, R.id.transfer_controls_stub)); } return transferControls.get(); } public void setBackgroundColorHint(int color) { this.backgroundColorHint = color; } public void setImageResource( @Nullable MasterSecret masterSecret, long id, long timestamp, @NonNull ListenableFutureTask<SlideDeck> slideDeckFuture) { if (this.slideDeckFuture != null && this.slideDeckListener != null) { this.slideDeckFuture.removeListener(this.slideDeckListener); } String slideId = id + "::" + timestamp; if (!slideId.equals(this.slideId)) { if (transferControls.isPresent()) getTransferControls().clear(); image.setImageDrawable(null); this.slide = null; this.slideId = slideId; } this.slideDeckListener = new SlideDeckListener(masterSecret); this.slideDeckFuture = slideDeckFuture; this.slideDeckFuture.addListener(this.slideDeckListener); } public void setImageResource(@NonNull Slide slide, @Nullable MasterSecret masterSecret) { if (Util.equals(slide, this.slide)) { Log.w(TAG, "Not re-loading slide " + slide.getPart().getPartId()); return; } if (!isContextValid()) { Log.w(TAG, "Not loading slide, context is invalid"); return; } Log.w( TAG, "loading part with id " + slide.getPart().getPartId() + ", progress " + slide.getTransferProgress()); this.slide = slide; loadInto(slide, masterSecret, image); if (!hideControls) { getTransferControls().setSlide(slide); getTransferControls().setDownloadClickListener(new DownloadClickDispatcher()); } } public void setThumbnailClickListener(ThumbnailClickListener listener) { this.thumbnailClickListener = listener; } public void setRemoveClickListener(OnClickListener listener) { getRemoveButton().setOnClickListener(listener); final int pad = getResources().getDimensionPixelSize(R.dimen.media_bubble_remove_button_size); image.setPadding(pad, pad, pad, 0); } public void setDownloadClickListener(ThumbnailClickListener listener) { this.downloadClickListener = listener; } public void clear() { if (isContextValid()) Glide.clear(image); if (slideDeckFuture != null) slideDeckFuture.removeListener(slideDeckListener); if (transferControls.isPresent()) getTransferControls().clear(); slide = null; slideId = null; slideDeckFuture = null; slideDeckListener = null; } public void hideControls(boolean hideControls) { this.hideControls = hideControls; if (hideControls && transferControls.isPresent()) getTransferControls().setVisibility(View.GONE); } public void showProgressSpinner() { getTransferControls().showProgressSpinner(); } @TargetApi(VERSION_CODES.JELLY_BEAN_MR1) private boolean isContextValid() { return !(getContext() instanceof Activity) || VERSION.SDK_INT < VERSION_CODES.JELLY_BEAN_MR1 || !((Activity) getContext()).isDestroyed(); } private void loadInto( @NonNull Slide slide, @Nullable MasterSecret masterSecret, @NonNull ImageView view) { if (slide.getThumbnailUri() != null) { buildThumbnailGlideRequest(slide, masterSecret).into(view); } else if (!slide.isInProgress()) { buildPlaceholderGlideRequest(slide).into(view); } else { Glide.clear(view); } } private GenericRequestBuilder buildThumbnailGlideRequest(Slide slide, MasterSecret masterSecret) { final GenericRequestBuilder builder; if (slide.isDraft()) builder = buildDraftGlideRequest(slide, masterSecret); else builder = buildPartGlideRequest(slide, masterSecret); if (slide.isInProgress()) return builder; else return builder.error(R.drawable.ic_missing_thumbnail_picture); } private GenericRequestBuilder buildDraftGlideRequest(Slide slide, MasterSecret masterSecret) { final DrawableTypeRequest<?> request; if (masterSecret == null) request = Glide.with(getContext()).load(slide.getThumbnailUri()); else request = Glide.with(getContext()).load(new DecryptableUri(masterSecret, slide.getThumbnailUri())); return request .transform(new RoundedCorners(getContext(), false, radius, backgroundColorHint)) .listener(new PduThumbnailSetListener(slide.getPart())); } private GenericRequestBuilder buildPartGlideRequest(Slide slide, MasterSecret masterSecret) { if (masterSecret == null) { throw new IllegalStateException("null MasterSecret when loading non-draft thumbnail"); } return Glide.with(getContext()) .load(new DecryptableUri(masterSecret, slide.getThumbnailUri())) .crossFade() .transform(new RoundedCorners(getContext(), true, radius, backgroundColorHint)); } private GenericRequestBuilder buildPlaceholderGlideRequest(Slide slide) { return Glide.with(getContext()) .load(slide.getPlaceholderRes(getContext().getTheme())) .asBitmap() .fitCenter(); } private class SlideDeckListener implements FutureTaskListener<SlideDeck> { private final MasterSecret masterSecret; public SlideDeckListener(MasterSecret masterSecret) { this.masterSecret = masterSecret; } @Override public void onSuccess(final SlideDeck slideDeck) { if (slideDeck == null) return; final Slide slide = slideDeck.getThumbnailSlide(); if (slide != null) { Util.runOnMain( new Runnable() { @Override public void run() { setImageResource(slide, masterSecret); } }); } else { Util.runOnMain( new Runnable() { @Override public void run() { Log.w(TAG, "Resolved slide was null!"); setVisibility(View.GONE); } }); } } @Override public void onFailure(Throwable error) { Log.w(TAG, error); Util.runOnMain( new Runnable() { @Override public void run() { Log.w(TAG, "onFailure!"); setVisibility(View.GONE); } }); } } public interface ThumbnailClickListener { void onClick(View v, Slide slide); } private class ThumbnailClickDispatcher implements View.OnClickListener { @Override public void onClick(View view) { if (thumbnailClickListener != null && slide != null && slide.getPart().getDataUri() != null && slide.getTransferProgress() == PartDatabase.TRANSFER_PROGRESS_DONE) { thumbnailClickListener.onClick(view, slide); } else if (parentClickListener != null) { parentClickListener.onClick(view); } } } private class DownloadClickDispatcher implements View.OnClickListener { @Override public void onClick(View view) { if (downloadClickListener != null && slide != null) { downloadClickListener.onClick(view, slide); } } } private class PduThumbnailSetListener implements RequestListener<Object, GlideDrawable> { private PduPart part; public PduThumbnailSetListener(@NonNull PduPart part) { this.part = part; } @Override public boolean onException( Exception e, Object model, Target<GlideDrawable> target, boolean isFirstResource) { return false; } @Override public boolean onResourceReady( GlideDrawable resource, Object model, Target<GlideDrawable> target, boolean isFromMemoryCache, boolean isFirstResource) { if (resource instanceof GlideBitmapDrawable) { Log.w(TAG, "onResourceReady() for a Bitmap. Saving."); part.setThumbnail(((GlideBitmapDrawable) resource).getBitmap()); } LayoutParams layoutParams = (LayoutParams) getRemoveButton().getLayoutParams(); if (resource.getIntrinsicWidth() < getWidth()) { layoutParams.topMargin = 0; layoutParams.rightMargin = Math.max(0, (getWidth() - image.getPaddingRight() - resource.getIntrinsicWidth()) / 2); } else { layoutParams.topMargin = Math.max(0, (getHeight() - image.getPaddingTop() - resource.getIntrinsicHeight()) / 2); layoutParams.rightMargin = 0; } getRemoveButton().setLayoutParams(layoutParams); return false; } } }
public class SendButton extends ImageButton implements TransportOptions.OnTransportChangedListener, TransportOptionsPopup.SelectedListener, View.OnLongClickListener { private final TransportOptions transportOptions; private Optional<TransportOptionsPopup> transportOptionsPopup = Optional.absent(); @SuppressWarnings("unused") public SendButton(Context context) { super(context); this.transportOptions = initializeTransportOptions(false); ViewUtil.mirrorIfRtl(this, getContext()); } @SuppressWarnings("unused") public SendButton(Context context, AttributeSet attrs) { super(context, attrs); this.transportOptions = initializeTransportOptions(false); ViewUtil.mirrorIfRtl(this, getContext()); } @SuppressWarnings("unused") public SendButton(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); this.transportOptions = initializeTransportOptions(false); ViewUtil.mirrorIfRtl(this, getContext()); } private TransportOptions initializeTransportOptions(boolean media) { TransportOptions transportOptions = new TransportOptions(getContext(), media); transportOptions.addOnTransportChangedListener(this); setOnLongClickListener(this); return transportOptions; } private TransportOptionsPopup getTransportOptionsPopup() { if (!transportOptionsPopup.isPresent()) { transportOptionsPopup = Optional.of(new TransportOptionsPopup(getContext(), this, this)); } return transportOptionsPopup.get(); } public boolean isManualSelection() { return transportOptions.isManualSelection(); } public void addOnTransportChangedListener(OnTransportChangedListener listener) { transportOptions.addOnTransportChangedListener(listener); } public TransportOption getSelectedTransport() { return transportOptions.getSelectedTransport(); } public void resetAvailableTransports(boolean isMediaMessage) { transportOptions.reset(isMediaMessage); } public void disableTransport(TransportOption.Type type) { transportOptions.disableTransport(type); } public void setDefaultTransport(TransportOption.Type type) { transportOptions.setDefaultTransport(type); } @Override public void onSelected(TransportOption option) { transportOptions.setSelectedTransport(option.getType()); getTransportOptionsPopup().dismiss(); } @Override public void onChange(TransportOption newTransport) { setImageResource(newTransport.getDrawable()); setContentDescription(newTransport.getDescription()); } @Override public boolean onLongClick(View v) { if (transportOptions.getEnabledTransports().size() > 1) { getTransportOptionsPopup().display(transportOptions.getEnabledTransports()); return true; } return false; } }