diff --git a/CHANGELOG.md b/CHANGELOG.md index 6ac520342..4224b4ed5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +### Version 2.5.3 +* bug fixes for peer to peer file transfer (Jingle) +* fixed server info for unlimited/unknown max file size + +### Version 2.5.2 +* bug fixes + ### Version 2.5.1 * minor bug fixes * Set own OMEMO devices to inactive after not seeing them for 14 days. (was 7 days) diff --git a/build.gradle b/build.gradle index 26d1216d1..81ebd74b3 100644 --- a/build.gradle +++ b/build.gradle @@ -81,8 +81,8 @@ android { defaultConfig { minSdkVersion 16 targetSdkVersion 25 - versionCode 329 - versionName "2.5.2" + versionCode 330 + versionName "2.5.3" archivesBaseName += "-$versionName" applicationId "eu.sum7.conversations" resValue "string", "applicationId", applicationId diff --git a/src/main/java/eu/siacs/conversations/Config.java b/src/main/java/eu/siacs/conversations/Config.java index 1e7c879ca..9de4f3fc7 100644 --- a/src/main/java/eu/siacs/conversations/Config.java +++ b/src/main/java/eu/siacs/conversations/Config.java @@ -150,7 +150,7 @@ public final class Config { "TLS_RSA_WITH_AES_256_CBC_SHA", }; - public static final String WEAK_CIPHER_PATTERNS[] = { + public static final String[] WEAK_CIPHER_PATTERNS = { "_NULL_", "_EXPORT_", "_anon_", diff --git a/src/main/java/eu/siacs/conversations/generator/AbstractGenerator.java b/src/main/java/eu/siacs/conversations/generator/AbstractGenerator.java index d320168bb..dc47fc4ed 100644 --- a/src/main/java/eu/siacs/conversations/generator/AbstractGenerator.java +++ b/src/main/java/eu/siacs/conversations/generator/AbstractGenerator.java @@ -27,8 +27,8 @@ public abstract class AbstractGenerator { Content.Version.FT_3.getNamespace(), Content.Version.FT_4.getNamespace(), Content.Version.FT_5.getNamespace(), - "urn:xmpp:jingle:transports:s5b:1", - "urn:xmpp:jingle:transports:ibb:1", + Namespace.JINGLE_TRANSPORTS_S5B, + Namespace.JINGLE_TRANSPORTS_IBB, "http://jabber.org/protocol/muc", "jabber:x:conference", Namespace.OOB, diff --git a/src/main/java/eu/siacs/conversations/http/HttpConnectionManager.java b/src/main/java/eu/siacs/conversations/http/HttpConnectionManager.java index 76fd83f08..d96bb4deb 100644 --- a/src/main/java/eu/siacs/conversations/http/HttpConnectionManager.java +++ b/src/main/java/eu/siacs/conversations/http/HttpConnectionManager.java @@ -36,7 +36,7 @@ public class HttpConnectionManager extends AbstractConnectionManager { } public static Proxy getProxy() throws IOException { - return new Proxy(Proxy.Type.HTTP, new InetSocketAddress(InetAddress.getByAddress(new byte[]{127, 0, 0, 1}), 8118)); + return new Proxy(Proxy.Type.SOCKS, new InetSocketAddress(InetAddress.getByAddress(new byte[]{127, 0, 0, 1}), 9050)); } public void createNewDownloadConnection(Message message) { diff --git a/src/main/java/eu/siacs/conversations/http/HttpDownloadConnection.java b/src/main/java/eu/siacs/conversations/http/HttpDownloadConnection.java index e67ac2db5..b5aed7212 100644 --- a/src/main/java/eu/siacs/conversations/http/HttpDownloadConnection.java +++ b/src/main/java/eu/siacs/conversations/http/HttpDownloadConnection.java @@ -276,7 +276,9 @@ public class HttpDownloadConnection implements Transferable { Log.d(Config.LOGTAG, "retrieve file size. interactive:" + String.valueOf(interactive)); changeStatus(STATUS_CHECKING); HttpURLConnection connection; - if (mUseTor || message.getConversation().getAccount().isOnion()) { + final String hostname = mUrl.getHost(); + final boolean onion = hostname != null && hostname.endsWith(".onion"); + if (mUseTor || message.getConversation().getAccount().isOnion() || onion) { connection = (HttpURLConnection) mUrl.openConnection(HttpConnectionManager.getProxy()); } else { connection = (HttpURLConnection) mUrl.openConnection(); diff --git a/src/main/java/eu/siacs/conversations/http/HttpUploadConnection.java b/src/main/java/eu/siacs/conversations/http/HttpUploadConnection.java index b3f5f021b..daa0c20c7 100644 --- a/src/main/java/eu/siacs/conversations/http/HttpUploadConnection.java +++ b/src/main/java/eu/siacs/conversations/http/HttpUploadConnection.java @@ -155,11 +155,14 @@ public class HttpUploadConnection implements Transferable { PowerManager.WakeLock wakeLock = mHttpConnectionManager.createWakeLock("http_upload_"+message.getUuid()); try { fileInputStream = new FileInputStream(file); + final String slotHostname = slot.getPutUrl().getHost(); + final boolean onionSlot = slotHostname != null && slotHostname.endsWith(".onion"); final int expectedFileSize = (int) file.getExpectedSize(); final int readTimeout = (expectedFileSize / 2048) + Config.SOCKET_TIMEOUT; //assuming a minimum transfer speed of 16kbit/s wakeLock.acquire(readTimeout); Log.d(Config.LOGTAG, "uploading to " + slot.getPutUrl().toString()+ " w/ read timeout of "+readTimeout+"s"); - if (mUseTor || message.getConversation().getAccount().isOnion()) { + + if (mUseTor || message.getConversation().getAccount().isOnion() || onionSlot) { connection = (HttpURLConnection) slot.getPutUrl().openConnection(HttpConnectionManager.getProxy()); } else { connection = (HttpURLConnection) slot.getPutUrl().openConnection(); diff --git a/src/main/java/eu/siacs/conversations/parser/MessageParser.java b/src/main/java/eu/siacs/conversations/parser/MessageParser.java index 10c9e9764..72aaf1fa0 100644 --- a/src/main/java/eu/siacs/conversations/parser/MessageParser.java +++ b/src/main/java/eu/siacs/conversations/parser/MessageParser.java @@ -869,7 +869,10 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece public boolean execute(Account account) { if (jid != null) { Conversation conversation = mXmppConnectionService.findOrCreateConversation(account, jid, true, false); - if (!conversation.getMucOptions().online()) { + if (conversation.getMucOptions().online()) { + Log.d(Config.LOGTAG,account.getJid().asBareJid()+": received invite to "+jid+" but muc is considered to be online"); + mXmppConnectionService.mucSelfPingAndRejoin(conversation); + } else { conversation.getMucOptions().setPassword(password); mXmppConnectionService.databaseBackend.updateConversation(conversation); mXmppConnectionService.joinMuc(conversation, inviter != null && inviter.mutualPresenceSubscription()); diff --git a/src/main/java/eu/siacs/conversations/services/NotificationService.java b/src/main/java/eu/siacs/conversations/services/NotificationService.java index 9de1df983..d6b9fc108 100644 --- a/src/main/java/eu/siacs/conversations/services/NotificationService.java +++ b/src/main/java/eu/siacs/conversations/services/NotificationService.java @@ -517,7 +517,6 @@ public class NotificationService { final Builder mBuilder = new NotificationCompat.Builder(mXmppConnectionService, quietHours ? "quiet_hours" : (notify ? "messages" : "silent_messages")); if (messages.size() >= 1) { final Conversation conversation = (Conversation) messages.get(0).getConversation(); - final UnreadConversation.Builder mUnreadBuilder = new UnreadConversation.Builder(conversation.getName().toString()); mBuilder.setLargeIcon(mXmppConnectionService.getAvatarService() .get(conversation, AvatarService.getSystemUiAvatarSize(mXmppConnectionService))); mBuilder.setContentTitle(conversation.getName()); @@ -528,28 +527,31 @@ public class NotificationService { Message message; //TODO starting with Android 9 we might want to put images in MessageStyle if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P && (message = getImage(messages)) != null) { - modifyForImage(mBuilder, mUnreadBuilder, message, messages); + modifyForImage(mBuilder, message, messages); } else { - modifyForTextOnly(mBuilder, mUnreadBuilder, messages); + modifyForTextOnly(mBuilder, messages); } RemoteInput remoteInput = new RemoteInput.Builder("text_reply").setLabel(UIHelper.getMessageHint(mXmppConnectionService, conversation)).build(); PendingIntent markAsReadPendingIntent = createReadPendingIntent(conversation); NotificationCompat.Action markReadAction = new NotificationCompat.Action.Builder( R.drawable.ic_drafts_white_24dp, mXmppConnectionService.getString(R.string.mark_as_read), - markAsReadPendingIntent).build(); + markAsReadPendingIntent) + .setSemanticAction(NotificationCompat.Action.SEMANTIC_ACTION_MARK_AS_READ) + .setShowsUserInterface(false) + .build(); String replyLabel = mXmppConnectionService.getString(R.string.reply); NotificationCompat.Action replyAction = new NotificationCompat.Action.Builder( R.drawable.ic_send_text_offline, replyLabel, - createReplyIntent(conversation, false)).addRemoteInput(remoteInput).build(); + createReplyIntent(conversation, false)) + .setSemanticAction(NotificationCompat.Action.SEMANTIC_ACTION_REPLY) + .setShowsUserInterface(false) + .addRemoteInput(remoteInput).build(); NotificationCompat.Action wearReplyAction = new NotificationCompat.Action.Builder(R.drawable.ic_wear_reply, replyLabel, createReplyIntent(conversation, true)).addRemoteInput(remoteInput).build(); mBuilder.extend(new NotificationCompat.WearableExtender().addAction(wearReplyAction)); - mUnreadBuilder.setReplyAction(createReplyIntent(conversation, true), remoteInput); - mUnreadBuilder.setReadPendingIntent(markAsReadPendingIntent); - mBuilder.extend(new NotificationCompat.CarExtender().setUnreadConversation(mUnreadBuilder.build())); int addedActionsCount = 1; mBuilder.addAction(markReadAction); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { @@ -609,8 +611,7 @@ public class NotificationService { return mBuilder; } - private void modifyForImage(final Builder builder, final UnreadConversation.Builder uBuilder, - final Message message, final ArrayList messages) { + private void modifyForImage(final Builder builder, final Message message, final ArrayList messages) { try { final Bitmap bitmap = mXmppConnectionService.getFileBackend().getThumbnail(message, getPixel(288), false); final ArrayList tmp = new ArrayList<>(); @@ -631,7 +632,7 @@ public class NotificationService { } builder.setStyle(bigPictureStyle); } catch (final IOException e) { - modifyForTextOnly(builder, uBuilder, messages); + modifyForTextOnly(builder, messages); } } @@ -653,7 +654,7 @@ public class NotificationService { return builder.build(); } - private void modifyForTextOnly(final Builder builder, final UnreadConversation.Builder uBuilder, final ArrayList messages) { + private void modifyForTextOnly(final Builder builder, final ArrayList messages) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { final Conversation conversation = (Conversation) messages.get(0).getConversation(); final Person.Builder meBuilder = new Person.Builder().setName(mXmppConnectionService.getString(R.string.me)); @@ -707,15 +708,6 @@ public class NotificationService { } } } - /** message preview for Android Auto **/ - for (Message message : messages) { - Pair preview = UIHelper.getMessagePreview(mXmppConnectionService, message); - // only show user written text - if (!preview.second) { - uBuilder.addMessage(preview.first.toString()); - uBuilder.setLatestTimestamp(message.getTimeSent()); - } - } } private Message getImage(final Iterable messages) { diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java index 4a88f3660..a596f3b87 100644 --- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java @@ -1559,7 +1559,8 @@ public class XmppConnectionService extends Service { } public void pushBookmarks(Account account) { - if (account.getXmppConnection().getFeatures().bookmarksConversion()) { + final XmppConnection connection = account.getXmppConnection(); + if (connection != null && connection.getFeatures().bookmarksConversion()) { pushBookmarksPep(account); } else { pushBookmarksPrivateXml(account); @@ -1973,7 +1974,7 @@ public class XmppConnectionService extends Service { archiveConversation(conversation, true); } - private void archiveConversation(Conversation conversation, final boolean maySyncronizeWithBookmarks) { + private void archiveConversation(Conversation conversation, final boolean maySynchronizeWithBookmarks) { getNotificationService().clear(conversation); conversation.setStatus(Conversation.STATUS_ARCHIVED); conversation.setNextMessage(null); @@ -1982,7 +1983,7 @@ public class XmppConnectionService extends Service { if (conversation.getMode() == Conversation.MODE_MULTI) { if (conversation.getAccount().getStatus() == Account.State.ONLINE) { Bookmark bookmark = conversation.getBookmark(); - if (maySyncronizeWithBookmarks && bookmark != null && synchronizeWithBookmarks()) { + if (maySynchronizeWithBookmarks && bookmark != null && synchronizeWithBookmarks()) { if (conversation.getMucOptions().getError() == MucOptions.Error.DESTROYED) { Account account = bookmark.getAccount(); bookmark.setConversation(null); @@ -2430,6 +2431,26 @@ public class XmppConnectionService extends Service { } } + public void mucSelfPingAndRejoin(final Conversation conversation) { + final Jid self = conversation.getMucOptions().getSelf().getFullJid(); + final IqPacket ping = new IqPacket(IqPacket.TYPE.GET); + ping.setTo(self); + ping.addChild("ping", Namespace.PING); + sendIqPacket(conversation.getAccount(), ping, (account, response) -> { + if (response.getType() == IqPacket.TYPE.ERROR) { + Element error = response.findChild("error"); + if (error == null || error.hasChild("service-unavailable") || error.hasChild("feature-not-implemented") || error.hasChild("item-not-found")) { + Log.d(Config.LOGTAG,account.getJid().asBareJid()+": ping to "+self+" came back as ignorable error"); + } else { + Log.d(Config.LOGTAG,account.getJid().asBareJid()+": ping to "+self+" failed. attempting rejoin"); + joinMuc(conversation); + } + } else if (response.getType() == IqPacket.TYPE.RESULT) { + Log.d(Config.LOGTAG,account.getJid().asBareJid()+": ping to "+self+" came back fine"); + } + }); + } + public void joinMuc(Conversation conversation) { joinMuc(conversation, null, false); } diff --git a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java index feefbd105..7a25a9d4d 100644 --- a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java +++ b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java @@ -1994,8 +1994,12 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke final boolean doNotAppend = extras.getBoolean(ConversationsActivity.EXTRA_DO_NOT_APPEND, false); final List uris = extractUris(extras); if (uris != null && uris.size() > 0) { - final List cleanedUris = cleanUris(new ArrayList<>(uris)); - mediaPreviewAdapter.addMediaPreviews(Attachment.of(getActivity(), cleanedUris)); + if (uris.size() == 1 && "geo".equals(uris.get(0).getScheme())) { + mediaPreviewAdapter.addMediaPreviews(Attachment.of(getActivity(), uris.get(0), Attachment.Type.LOCATION)); + } else { + final List cleanedUris = cleanUris(new ArrayList<>(uris)); + mediaPreviewAdapter.addMediaPreviews(Attachment.of(getActivity(), cleanedUris)); + } toggleInputMethod(); return; } @@ -2015,7 +2019,11 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke } } } else { - if (text != null && asQuote) { + if (text != null && GeoHelper.GEO_URI.matcher(text).matches()) { + mediaPreviewAdapter.addMediaPreviews(Attachment.of(getActivity(), Uri.parse(text), Attachment.Type.LOCATION)); + toggleInputMethod(); + return; + } else if (text != null && asQuote) { quoteText(text); } else { appendText(text, doNotAppend); diff --git a/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java b/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java index 7c7f3b847..71a4b9ae3 100644 --- a/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java @@ -1044,7 +1044,12 @@ public class EditAccountActivity extends OmemoActivity implements OnAccountUpdat this.binding.serverInfoPep.setText(R.string.server_info_unavailable); } if (features.httpUpload(0)) { - this.binding.serverInfoHttpUpload.setText(UIHelper.filesizeToString(features.getMaxHttpUploadSize())); + final long maxFileSize = features.getMaxHttpUploadSize(); + if (maxFileSize > 0) { + this.binding.serverInfoHttpUpload.setText(UIHelper.filesizeToString(maxFileSize)); + } else { + this.binding.serverInfoHttpUpload.setText(R.string.server_info_available); + } } else if (features.p1S3FileTransfer()) { this.binding.serverInfoHttpUploadDescription.setText(R.string.p1_s3_filetransfer); this.binding.serverInfoHttpUpload.setText(R.string.server_info_available); diff --git a/src/main/java/eu/siacs/conversations/ui/ShareWithActivity.java b/src/main/java/eu/siacs/conversations/ui/ShareWithActivity.java index 40ac1599c..9d6385db8 100644 --- a/src/main/java/eu/siacs/conversations/ui/ShareWithActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/ShareWithActivity.java @@ -21,6 +21,7 @@ import eu.siacs.conversations.entities.Conversation; import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.ui.adapter.ConversationAdapter; import eu.siacs.conversations.ui.service.EmojiService; +import eu.siacs.conversations.utils.GeoHelper; import rocks.xmpp.addr.Jid; public class ShareWithActivity extends XmppActivity implements XmppConnectionService.OnConversationUpdate { @@ -126,10 +127,14 @@ public class ShareWithActivity extends XmppActivity implements XmppConnectionSer } final String type = intent.getType(); final String action = intent.getAction(); + final Uri data = intent.getData(); if (Intent.ACTION_SEND.equals(action)) { final String text = intent.getStringExtra(Intent.EXTRA_TEXT); final Uri uri = intent.getParcelableExtra(Intent.EXTRA_STREAM); - if (type != null && uri != null && (text == null || !type.equals("text/plain"))) { + if (data != null && "geo".equals(data.getScheme())) { + this.share.uris.clear(); + this.share.uris.add(data); + } else if (type != null && uri != null && (text == null || !type.equals("text/plain"))) { this.share.uris.clear(); this.share.uris.add(uri); } else { diff --git a/src/main/java/eu/siacs/conversations/ui/util/SendButtonAction.java b/src/main/java/eu/siacs/conversations/ui/util/SendButtonAction.java index 074f57659..044bcdbf3 100644 --- a/src/main/java/eu/siacs/conversations/ui/util/SendButtonAction.java +++ b/src/main/java/eu/siacs/conversations/ui/util/SendButtonAction.java @@ -38,7 +38,10 @@ import static eu.siacs.conversations.ui.ConversationFragment.ATTACHMENT_CHOICE_T public enum SendButtonAction { TEXT, TAKE_PHOTO, SEND_LOCATION, RECORD_VOICE, CANCEL, CHOOSE_PICTURE, RECORD_VIDEO; - public static SendButtonAction valueOfOrDefault(String setting, SendButtonAction text) { + public static SendButtonAction valueOfOrDefault(final String setting) { + if (setting == null) { + return TEXT; + } try { return valueOf(setting); } catch (IllegalArgumentException e) { diff --git a/src/main/java/eu/siacs/conversations/ui/util/SendButtonTool.java b/src/main/java/eu/siacs/conversations/ui/util/SendButtonTool.java index 9a92fbba9..66a355fe4 100644 --- a/src/main/java/eu/siacs/conversations/ui/util/SendButtonTool.java +++ b/src/main/java/eu/siacs/conversations/ui/util/SendButtonTool.java @@ -42,7 +42,10 @@ import eu.siacs.conversations.utils.UIHelper; public class SendButtonTool { - public static SendButtonAction getAction(Activity activity, Conversation c, String text) { + public static SendButtonAction getAction(final Activity activity, final Conversation c, final String text) { + if (activity == null) { + return SendButtonAction.TEXT; + } final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(activity); final boolean empty = text.length() == 0; final boolean conference = c.getMode() == Conversation.MODE_MULTI; @@ -60,14 +63,14 @@ public class SendButtonTool { return SendButtonAction.CANCEL; } else { String setting = preferences.getString("quick_action", activity.getResources().getString(R.string.quick_action)); - if (!setting.equals("none") && UIHelper.receivedLocationQuestion(c.getLatestMessage())) { + if (!"none".equals(setting) && UIHelper.receivedLocationQuestion(c.getLatestMessage())) { return SendButtonAction.SEND_LOCATION; } else { - if (setting.equals("recent")) { + if ("recent".equals(setting)) { setting = preferences.getString(ConversationFragment.RECENTLY_USED_QUICK_ACTION, SendButtonAction.TEXT.toString()); - return SendButtonAction.valueOfOrDefault(setting, SendButtonAction.TEXT); + return SendButtonAction.valueOfOrDefault(setting); } else { - return SendButtonAction.valueOfOrDefault(setting, SendButtonAction.TEXT); + return SendButtonAction.valueOfOrDefault(setting); } } } diff --git a/src/main/java/eu/siacs/conversations/utils/GeoHelper.java b/src/main/java/eu/siacs/conversations/utils/GeoHelper.java index e71b1bfa6..79600075a 100644 --- a/src/main/java/eu/siacs/conversations/utils/GeoHelper.java +++ b/src/main/java/eu/siacs/conversations/utils/GeoHelper.java @@ -1,6 +1,5 @@ package eu.siacs.conversations.utils; -import android.app.Activity; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; @@ -17,7 +16,6 @@ import java.util.regex.Pattern; import eu.siacs.conversations.R; import eu.siacs.conversations.entities.Contact; -import eu.siacs.conversations.entities.Conversation; import eu.siacs.conversations.entities.Conversational; import eu.siacs.conversations.entities.Message; import eu.siacs.conversations.ui.ShareLocationActivity; @@ -28,7 +26,7 @@ public class GeoHelper { private static final String SHARE_LOCATION_PACKAGE_NAME = "eu.siacs.conversations.location.request"; private static final String SHOW_LOCATION_PACKAGE_NAME = "eu.siacs.conversations.location.show"; - public static Pattern GEO_URI = Pattern.compile("geo:(-?\\d+(?:\\.\\d+)?),(-?\\d+(?:\\.\\d+)?)(?:,-?\\d+(?:\\.\\d+)?)?(?:;crs=[\\w-]+)?(?:;u=\\d+(?:\\.\\d+)?)?(?:;[\\w-]+=(?:[\\w-_.!~*'()]|%[\\da-f][\\da-f])+)*", Pattern.CASE_INSENSITIVE); + public static Pattern GEO_URI = Pattern.compile("geo:(-?\\d+(?:\\.\\d+)?),(-?\\d+(?:\\.\\d+)?)(?:,-?\\d+(?:\\.\\d+)?)?(?:;crs=[\\w-]+)?(?:;u=\\d+(?:\\.\\d+)?)?(?:;[\\w-]+=(?:[\\w-_.!~*'()]|%[\\da-f][\\da-f])+)*(\\?z=\\d+)?", Pattern.CASE_INSENSITIVE); public static boolean isLocationPluginInstalled(Context context) { return new Intent(SHARE_LOCATION_PACKAGE_NAME).resolveActivity(context.getPackageManager()) != null; diff --git a/src/main/java/eu/siacs/conversations/xml/Namespace.java b/src/main/java/eu/siacs/conversations/xml/Namespace.java index f58f06373..73d3d452d 100644 --- a/src/main/java/eu/siacs/conversations/xml/Namespace.java +++ b/src/main/java/eu/siacs/conversations/xml/Namespace.java @@ -25,4 +25,7 @@ public final class Namespace { public static final String BOOKMARKS = "storage:bookmarks"; public static final String SYNCHRONIZATION = "im.quicksy.synchronization:0"; public static final String AVATAR_CONVERSION = "urn:xmpp:pep-vcard-conversion:0"; + public static final String JINGLE_TRANSPORTS_S5B = "urn:xmpp:jingle:transports:s5b:1"; + public static final String JINGLE_TRANSPORTS_IBB = "urn:xmpp:jingle:transports:ibb:1"; + public static final String PING = "urn:xmpp:ping"; } diff --git a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java index 7dea4b859..a0fc5837d 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java @@ -1396,7 +1396,7 @@ public class XmppConnection implements Runnable { if (!r()) { final IqPacket iq = new IqPacket(IqPacket.TYPE.GET); iq.setFrom(account.getJid()); - iq.addChild("ping", "urn:xmpp:ping"); + iq.addChild("ping", Namespace.PING); this.sendIqPacket(iq, null); } this.lastPingSent = SystemClock.elapsedRealtime(); diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java index 843079aea..1ec5f2714 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java @@ -2,7 +2,6 @@ package eu.siacs.conversations.xmpp.jingle; import android.util.Base64; import android.util.Log; -import android.util.Pair; import java.io.FileInputStream; import java.io.FileNotFoundException; @@ -11,6 +10,7 @@ import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Locale; @@ -35,6 +35,7 @@ import eu.siacs.conversations.services.AbstractConnectionManager; import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.utils.CryptoHelper; import eu.siacs.conversations.xml.Element; +import eu.siacs.conversations.xml.Namespace; import eu.siacs.conversations.xmpp.OnIqPacketReceived; import eu.siacs.conversations.xmpp.jingle.stanzas.Content; import eu.siacs.conversations.xmpp.jingle.stanzas.JinglePacket; @@ -44,1079 +45,1112 @@ import rocks.xmpp.addr.Jid; public class JingleConnection implements Transferable { - private JingleConnectionManager mJingleConnectionManager; - private XmppConnectionService mXmppConnectionService; + private static final int JINGLE_STATUS_INITIATED = 0; + private static final int JINGLE_STATUS_ACCEPTED = 1; + private static final int JINGLE_STATUS_FINISHED = 4; + static final int JINGLE_STATUS_TRANSMITTING = 5; + private static final int JINGLE_STATUS_FAILED = 99; + private static final int JINGLE_STATUS_OFFERED = -1; + private JingleConnectionManager mJingleConnectionManager; + private XmppConnectionService mXmppConnectionService; + private Content.Version ftVersion = Content.Version.FT_3; - private static final int JINGLE_STATUS_OFFERED = -1; - protected static final int JINGLE_STATUS_INITIATED = 0; - protected static final int JINGLE_STATUS_ACCEPTED = 1; - protected static final int JINGLE_STATUS_FINISHED = 4; - protected static final int JINGLE_STATUS_TRANSMITTING = 5; - protected static final int JINGLE_STATUS_FAILED = 99; + private int ibbBlockSize = 8192; - private Content.Version ftVersion = Content.Version.FT_3; + private int mJingleStatus = JINGLE_STATUS_OFFERED; + private int mStatus = Transferable.STATUS_UNKNOWN; + private Message message; + private String sessionId; + private Account account; + private Jid initiator; + private Jid responder; + private List candidates = new ArrayList<>(); + private ConcurrentHashMap connections = new ConcurrentHashMap<>(); - private int ibbBlockSize = 8192; + private String transportId; + private Element fileOffer; + private DownloadableFile file = null; - private int mJingleStatus = JINGLE_STATUS_OFFERED; - private int mStatus = Transferable.STATUS_UNKNOWN; - private Message message; - private String sessionId; - private Account account; - private Jid initiator; - private Jid responder; - private List candidates = new ArrayList<>(); - private ConcurrentHashMap connections = new ConcurrentHashMap<>(); + private String contentName; + private String contentCreator; + private Transport initialTransport; - private String transportId; - private Element fileOffer; - private DownloadableFile file = null; + private int mProgress = 0; - private String contentName; - private String contentCreator; + private boolean receivedCandidate = false; + private boolean sentCandidate = false; - private int mProgress = 0; + private boolean acceptedAutomatically = false; + private boolean cancelled = false; - private boolean receivedCandidate = false; - private boolean sentCandidate = false; + private XmppAxolotlMessage mXmppAxolotlMessage; - private boolean acceptedAutomatically = false; - private boolean cancelled = false; + private JingleTransport transport = null; - private XmppAxolotlMessage mXmppAxolotlMessage; + private OutputStream mFileOutputStream; + private InputStream mFileInputStream; - private JingleTransport transport = null; + private OnIqPacketReceived responseListener = (account, packet) -> { + if (packet.getType() != IqPacket.TYPE.RESULT) { + fail(IqParser.extractErrorMessage(packet)); + } + }; + private byte[] expectedHash = new byte[0]; + private final OnFileTransmissionStatusChanged onFileTransmissionStatusChanged = new OnFileTransmissionStatusChanged() { - private OutputStream mFileOutputStream; - private InputStream mFileInputStream; + @Override + public void onFileTransmitted(DownloadableFile file) { + if (responding()) { + if (expectedHash.length > 0 && !Arrays.equals(expectedHash, file.getSha1Sum())) { + Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": hashes did not match"); + } + Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": file transmitted(). we are responding"); + sendSuccess(); + mXmppConnectionService.getFileBackend().updateFileParams(message); + mXmppConnectionService.databaseBackend.createMessage(message); + mXmppConnectionService.markMessage(message, Message.STATUS_RECEIVED); + if (acceptedAutomatically) { + message.markUnread(); + if (message.getEncryption() == Message.ENCRYPTION_PGP) { + account.getPgpDecryptionService().decrypt(message, true); + } else { + mXmppConnectionService.getFileBackend().updateMediaScanner(file, () -> JingleConnection.this.mXmppConnectionService.getNotificationService().push(message)); - private OnIqPacketReceived responseListener = (account, packet) -> { - if (packet.getType() != IqPacket.TYPE.RESULT) { - fail(IqParser.extractErrorMessage(packet)); - } - }; - private byte[] expectedHash = new byte[0]; + } + Log.d(Config.LOGTAG, "successfully transmitted file:" + file.getAbsolutePath() + " (" + CryptoHelper.bytesToHex(file.getSha1Sum()) + ")"); + return; + } + } else { + if (ftVersion == Content.Version.FT_5) { //older Conversations will break when receiving a session-info + sendHash(); + } + if (message.getEncryption() == Message.ENCRYPTION_PGP) { + account.getPgpDecryptionService().decrypt(message, false); + } + if (message.getEncryption() == Message.ENCRYPTION_PGP || message.getEncryption() == Message.ENCRYPTION_DECRYPTED) { + file.delete(); + } + } + Log.d(Config.LOGTAG, "successfully transmitted file:" + file.getAbsolutePath() + " (" + CryptoHelper.bytesToHex(file.getSha1Sum()) + ")"); + if (message.getEncryption() != Message.ENCRYPTION_PGP) { + mXmppConnectionService.getFileBackend().updateMediaScanner(file); + } + } - private boolean responding() { - return responder != null && responder.equals(account.getJid()); - } + @Override + public void onFileTransferAborted() { + JingleConnection.this.sendCancel(); + JingleConnection.this.fail(); + } + }; + private OnTransportConnected onIbbTransportConnected = new OnTransportConnected() { + @Override + public void failed() { + Log.d(Config.LOGTAG, "ibb open failed"); + } - private boolean initiating() { - return initiator.equals(account.getJid()); - } + @Override + public void established() { + Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": ibb transport connected. sending file"); + mJingleStatus = JINGLE_STATUS_TRANSMITTING; + JingleConnection.this.transport.send(file, onFileTransmissionStatusChanged); + } + }; + private OnProxyActivated onProxyActivated = new OnProxyActivated() { - final OnFileTransmissionStatusChanged onFileTransmissionStatusChanged = new OnFileTransmissionStatusChanged() { + @Override + public void success() { + if (initiator.equals(account.getJid())) { + Log.d(Config.LOGTAG, "we were initiating. sending file"); + transport.send(file, onFileTransmissionStatusChanged); + } else { + transport.receive(file, onFileTransmissionStatusChanged); + Log.d(Config.LOGTAG, "we were responding. receiving file"); + } + } - @Override - public void onFileTransmitted(DownloadableFile file) { - if (responding()) { - if (expectedHash.length > 0 && !Arrays.equals(expectedHash,file.getSha1Sum())) { - Log.d(Config.LOGTAG,account.getJid().asBareJid()+": hashes did not match"); - } - sendSuccess(); - mXmppConnectionService.getFileBackend().updateFileParams(message); - mXmppConnectionService.databaseBackend.createMessage(message); - mXmppConnectionService.markMessage(message,Message.STATUS_RECEIVED); - if (acceptedAutomatically) { - message.markUnread(); - if (message.getEncryption() == Message.ENCRYPTION_PGP) { - account.getPgpDecryptionService().decrypt(message, true); - } else { - mXmppConnectionService.getFileBackend().updateMediaScanner(file, () -> JingleConnection.this.mXmppConnectionService.getNotificationService().push(message)); + @Override + public void failed() { + Log.d(Config.LOGTAG, "proxy activation failed"); + } + }; - } - Log.d(Config.LOGTAG,"successfully transmitted file:" + file.getAbsolutePath()+" ("+ CryptoHelper.bytesToHex(file.getSha1Sum())+")"); - return; - } - } else { - if (ftVersion == Content.Version.FT_5) { //older Conversations will break when receiving a session-info - sendHash(); - } - if (message.getEncryption() == Message.ENCRYPTION_PGP) { - account.getPgpDecryptionService().decrypt(message, false); - } - if (message.getEncryption() == Message.ENCRYPTION_PGP || message.getEncryption() == Message.ENCRYPTION_DECRYPTED) { - file.delete(); - } - } - Log.d(Config.LOGTAG,"successfully transmitted file:" + file.getAbsolutePath()+" ("+ CryptoHelper.bytesToHex(file.getSha1Sum())+")"); - if (message.getEncryption() != Message.ENCRYPTION_PGP) { - mXmppConnectionService.getFileBackend().updateMediaScanner(file); - } - } + public JingleConnection(JingleConnectionManager mJingleConnectionManager) { + this.mJingleConnectionManager = mJingleConnectionManager; + this.mXmppConnectionService = mJingleConnectionManager + .getXmppConnectionService(); + } - @Override - public void onFileTransferAborted() { - JingleConnection.this.sendCancel(); - JingleConnection.this.fail(); - } - }; + private boolean responding() { + return responder != null && responder.equals(account.getJid()); + } - InputStream getFileInputStream() { - return this.mFileInputStream; - } + private boolean initiating() { + return initiator.equals(account.getJid()); + } - OutputStream getFileOutputStream() throws IOException { - if (this.file == null) { - Log.d(Config.LOGTAG,"file object was not assigned"); - return null; - } - this.file.getParentFile().mkdirs(); - this.file.createNewFile(); - this.mFileOutputStream = AbstractConnectionManager.createOutputStream(this.file); - return this.mFileOutputStream; - } + InputStream getFileInputStream() { + return this.mFileInputStream; + } - private OnProxyActivated onProxyActivated = new OnProxyActivated() { + OutputStream getFileOutputStream() throws IOException { + if (this.file == null) { + Log.d(Config.LOGTAG, "file object was not assigned"); + return null; + } + this.file.getParentFile().mkdirs(); + this.file.createNewFile(); + this.mFileOutputStream = AbstractConnectionManager.createOutputStream(this.file); + return this.mFileOutputStream; + } - @Override - public void success() { - if (initiator.equals(account.getJid())) { - Log.d(Config.LOGTAG, "we were initiating. sending file"); - transport.send(file, onFileTransmissionStatusChanged); - } else { - transport.receive(file, onFileTransmissionStatusChanged); - Log.d(Config.LOGTAG, "we were responding. receiving file"); - } - } + public String getSessionId() { + return this.sessionId; + } - @Override - public void failed() { - Log.d(Config.LOGTAG, "proxy activation failed"); - } - }; + public Account getAccount() { + return this.account; + } - public JingleConnection(JingleConnectionManager mJingleConnectionManager) { - this.mJingleConnectionManager = mJingleConnectionManager; - this.mXmppConnectionService = mJingleConnectionManager - .getXmppConnectionService(); - } + public Jid getCounterPart() { + return this.message.getCounterpart(); + } - public String getSessionId() { - return this.sessionId; - } + public void deliverPacket(JinglePacket packet) { + boolean returnResult = true; + if (packet.isAction("session-terminate")) { + Reason reason = packet.getReason(); + if (reason != null) { + if (reason.hasChild("cancel")) { + this.fail(); + } else if (reason.hasChild("success")) { + this.receiveSuccess(); + } else { + this.fail(); + } + } else { + this.fail(); + } + } else if (packet.isAction("session-accept")) { + returnResult = receiveAccept(packet); + } else if (packet.isAction("session-info")) { + Element checksum = packet.getChecksum(); + Element file = checksum == null ? null : checksum.findChild("file"); + Element hash = file == null ? null : file.findChild("hash", "urn:xmpp:hashes:2"); + if (hash != null && "sha-1".equalsIgnoreCase(hash.getAttribute("algo"))) { + try { + this.expectedHash = Base64.decode(hash.getContent(), Base64.DEFAULT); + } catch (Exception e) { + this.expectedHash = new byte[0]; + } + } + } else if (packet.isAction("transport-info")) { + returnResult = receiveTransportInfo(packet); + } else if (packet.isAction("transport-replace")) { + if (packet.getJingleContent().hasIbbTransport()) { + returnResult = this.receiveFallbackToIbb(packet); + } else { + returnResult = false; + Log.d(Config.LOGTAG, "trying to fallback to something unknown" + + packet.toString()); + } + } else if (packet.isAction("transport-accept")) { + returnResult = this.receiveTransportAccept(packet); + } else { + Log.d(Config.LOGTAG, "packet arrived in connection. action was " + + packet.getAction()); + returnResult = false; + } + IqPacket response; + if (returnResult) { + response = packet.generateResponse(IqPacket.TYPE.RESULT); - public Account getAccount() { - return this.account; - } + } else { + response = packet.generateResponse(IqPacket.TYPE.ERROR); + } + mXmppConnectionService.sendIqPacket(account, response, null); + } - public Jid getCounterPart() { - return this.message.getCounterpart(); - } + public void init(final Message message) { + if (message.getEncryption() == Message.ENCRYPTION_AXOLOTL) { + Conversation conversation = (Conversation) message.getConversation(); + conversation.getAccount().getAxolotlService().prepareKeyTransportMessage(conversation, xmppAxolotlMessage -> { + if (xmppAxolotlMessage != null) { + init(message, xmppAxolotlMessage); + } else { + fail(); + } + }); + } else { + init(message, null); + } + } - public void deliverPacket(JinglePacket packet) { - boolean returnResult = true; - if (packet.isAction("session-terminate")) { - Reason reason = packet.getReason(); - if (reason != null) { - if (reason.hasChild("cancel")) { - this.fail(); - } else if (reason.hasChild("success")) { - this.receiveSuccess(); - } else { - this.fail(); - } - } else { - this.fail(); - } - } else if (packet.isAction("session-accept")) { - returnResult = receiveAccept(packet); - } else if (packet.isAction("session-info")) { - returnResult = true; - Element checksum = packet.getChecksum(); - Element file = checksum == null ? null : checksum.findChild("file"); - Element hash = file == null ? null : file.findChild("hash","urn:xmpp:hashes:2"); - if (hash != null && "sha-1".equalsIgnoreCase(hash.getAttribute("algo"))) { - try { - this.expectedHash = Base64.decode(hash.getContent(), Base64.DEFAULT); - } catch (Exception e) { - this.expectedHash = new byte[0]; - } - } - } else if (packet.isAction("transport-info")) { - returnResult = receiveTransportInfo(packet); - } else if (packet.isAction("transport-replace")) { - if (packet.getJingleContent().hasIbbTransport()) { - returnResult = this.receiveFallbackToIbb(packet); - } else { - returnResult = false; - Log.d(Config.LOGTAG, "trying to fallback to something unknown" - + packet.toString()); - } - } else if (packet.isAction("transport-accept")) { - returnResult = this.receiveTransportAccept(packet); - } else { - Log.d(Config.LOGTAG, "packet arrived in connection. action was " - + packet.getAction()); - returnResult = false; - } - IqPacket response; - if (returnResult) { - response = packet.generateResponse(IqPacket.TYPE.RESULT); + private void init(Message message, XmppAxolotlMessage xmppAxolotlMessage) { + this.mXmppAxolotlMessage = xmppAxolotlMessage; + this.contentCreator = "initiator"; + this.contentName = this.mJingleConnectionManager.nextRandomId(); + this.message = message; + this.account = message.getConversation().getAccount(); + upgradeNamespace(); + this.initialTransport = getRemoteFeatures().contains(Namespace.JINGLE_TRANSPORTS_S5B) ? Transport.SOCKS : Transport.IBB; + this.message.setTransferable(this); + this.mStatus = Transferable.STATUS_UPLOADING; + this.initiator = this.account.getJid(); + this.responder = this.message.getCounterpart(); + this.sessionId = this.mJingleConnectionManager.nextRandomId(); + this.transportId = this.mJingleConnectionManager.nextRandomId(); + if (this.initialTransport == Transport.IBB) { + this.sendInitRequest(); + } else if (this.candidates.size() > 0) { + this.sendInitRequest(); + } else { + this.mJingleConnectionManager.getPrimaryCandidate(account, (success, candidate) -> { + if (success) { + final JingleSocks5Transport socksConnection = new JingleSocks5Transport(this, candidate); + connections.put(candidate.getCid(), socksConnection); + socksConnection.connect(new OnTransportConnected() { - } else { - response = packet.generateResponse(IqPacket.TYPE.ERROR); - } - mXmppConnectionService.sendIqPacket(account,response,null); - } + @Override + public void failed() { + Log.d(Config.LOGTAG, + "connection to our own primary candidete failed"); + sendInitRequest(); + } - public void init(final Message message) { - if (message.getEncryption() == Message.ENCRYPTION_AXOLOTL) { - Conversation conversation = (Conversation) message.getConversation(); - conversation.getAccount().getAxolotlService().prepareKeyTransportMessage(conversation, new OnMessageCreatedCallback() { - @Override - public void run(XmppAxolotlMessage xmppAxolotlMessage) { - if (xmppAxolotlMessage != null) { - init(message, xmppAxolotlMessage); - } else { - fail(); - } - } - }); - } else { - init(message, null); - } - } + @Override + public void established() { + Log.d(Config.LOGTAG, + "successfully connected to our own primary candidate"); + mergeCandidate(candidate); + sendInitRequest(); + } + }); + mergeCandidate(candidate); + } else { + Log.d(Config.LOGTAG, "no primary candidate of our own was found"); + sendInitRequest(); + } + }); + } - private void init(Message message, XmppAxolotlMessage xmppAxolotlMessage) { - this.mXmppAxolotlMessage = xmppAxolotlMessage; - this.contentCreator = "initiator"; - this.contentName = this.mJingleConnectionManager.nextRandomId(); - this.message = message; - this.account = message.getConversation().getAccount(); - upgradeNamespace(); - this.message.setTransferable(this); - this.mStatus = Transferable.STATUS_UPLOADING; - this.initiator = this.account.getJid(); - this.responder = this.message.getCounterpart(); - this.sessionId = this.mJingleConnectionManager.nextRandomId(); - this.transportId = this.mJingleConnectionManager.nextRandomId(); - if (this.candidates.size() > 0) { - this.sendInitRequest(); - } else { - this.mJingleConnectionManager.getPrimaryCandidate(account, - new OnPrimaryCandidateFound() { + } - @Override - public void onPrimaryCandidateFound(boolean success, - final JingleCandidate candidate) { - if (success) { - final JingleSocks5Transport socksConnection = new JingleSocks5Transport( - JingleConnection.this, candidate); - connections.put(candidate.getCid(), - socksConnection); - socksConnection - .connect(new OnTransportConnected() { + private void upgradeNamespace() { + List features = getRemoteFeatures(); + if (features.contains(Content.Version.FT_5.getNamespace())) { + this.ftVersion = Content.Version.FT_5; + } else if (features.contains(Content.Version.FT_4.getNamespace())) { + this.ftVersion = Content.Version.FT_4; + } + } - @Override - public void failed() { - Log.d(Config.LOGTAG, - "connection to our own primary candidete failed"); - sendInitRequest(); - } + private List getRemoteFeatures() { + Jid jid = this.message.getCounterpart(); + String resource = jid != null ? jid.getResource() : null; + if (resource != null) { + Presence presence = this.account.getRoster().getContact(jid).getPresences().getPresences().get(resource); + ServiceDiscoveryResult result = presence != null ? presence.getServiceDiscoveryResult() : null; + return result == null ? Collections.emptyList() : result.getFeatures(); + } else { + return Collections.emptyList(); + } + } - @Override - public void established() { - Log.d(Config.LOGTAG, - "successfully connected to our own primary candidate"); - mergeCandidate(candidate); - sendInitRequest(); - } - }); - mergeCandidate(candidate); - } else { - Log.d(Config.LOGTAG, "no primary candidate of our own was found"); - sendInitRequest(); - } - } - }); - } - - } - - private void upgradeNamespace() { - Jid jid = this.message.getCounterpart(); - String resource = jid != null ?jid.getResource() : null; - if (resource != null) { - Presence presence = this.account.getRoster().getContact(jid).getPresences().getPresences().get(resource); - ServiceDiscoveryResult result = presence != null ? presence.getServiceDiscoveryResult() : null; - if (result != null) { - List features = result.getFeatures(); - if (features.contains(Content.Version.FT_5.getNamespace())) { - this.ftVersion = Content.Version.FT_5; - } else if (features.contains(Content.Version.FT_4.getNamespace())) { - this.ftVersion = Content.Version.FT_4; - } - } - } - } - - public void init(Account account, JinglePacket packet) { - this.mJingleStatus = JINGLE_STATUS_INITIATED; - Conversation conversation = this.mXmppConnectionService - .findOrCreateConversation(account, - packet.getFrom().asBareJid(), false, false); - this.message = new Message(conversation, "", Message.ENCRYPTION_NONE); - this.message.setStatus(Message.STATUS_RECEIVED); - this.mStatus = Transferable.STATUS_OFFER; - this.message.setTransferable(this); + public void init(Account account, JinglePacket packet) { + this.mJingleStatus = JINGLE_STATUS_INITIATED; + Conversation conversation = this.mXmppConnectionService + .findOrCreateConversation(account, + packet.getFrom().asBareJid(), false, false); + this.message = new Message(conversation, "", Message.ENCRYPTION_NONE); + this.message.setStatus(Message.STATUS_RECEIVED); + this.mStatus = Transferable.STATUS_OFFER; + this.message.setTransferable(this); final Jid from = packet.getFrom(); - this.message.setCounterpart(from); - this.account = account; - this.initiator = packet.getFrom(); - this.responder = this.account.getJid(); - this.sessionId = packet.getSessionId(); - Content content = packet.getJingleContent(); - this.contentCreator = content.getAttribute("creator"); - this.contentName = content.getAttribute("name"); - this.transportId = content.getTransportId(); - this.mergeCandidates(JingleCandidate.parse(content.socks5transport().getChildren())); - this.ftVersion = content.getVersion(); - if (ftVersion == null) { - this.sendCancel(); - this.fail(); - return; - } - this.fileOffer = content.getFileOffer(this.ftVersion); + this.message.setCounterpart(from); + this.account = account; + this.initiator = packet.getFrom(); + this.responder = this.account.getJid(); + this.sessionId = packet.getSessionId(); + Content content = packet.getJingleContent(); + this.contentCreator = content.getAttribute("creator"); + this.initialTransport = content.hasSocks5Transport() ? Transport.SOCKS : Transport.IBB; + this.contentName = content.getAttribute("name"); + this.transportId = content.getTransportId(); - mXmppConnectionService.sendIqPacket(account,packet.generateResponse(IqPacket.TYPE.RESULT),null); + mXmppConnectionService.sendIqPacket(account, packet.generateResponse(IqPacket.TYPE.RESULT), null); - if (fileOffer != null) { - Element encrypted = fileOffer.findChild("encrypted", AxolotlService.PEP_PREFIX); - if (encrypted != null) { - this.mXmppAxolotlMessage = XmppAxolotlMessage.fromElement(encrypted, packet.getFrom().asBareJid()); - } - Element fileSize = fileOffer.findChild("size"); - Element fileNameElement = fileOffer.findChild("name"); - if (fileNameElement != null) { - String[] filename = fileNameElement.getContent() - .toLowerCase(Locale.US).toLowerCase().split("\\."); - String extension = filename[filename.length - 1]; - if (VALID_IMAGE_EXTENSIONS.contains(extension)) { - message.setType(Message.TYPE_IMAGE); - message.setRelativeFilePath(message.getUuid()+"."+extension); - } else if (VALID_CRYPTO_EXTENSIONS.contains( - filename[filename.length - 1])) { - if (filename.length == 3) { - extension = filename[filename.length - 2]; - if (VALID_IMAGE_EXTENSIONS.contains(extension)) { - message.setType(Message.TYPE_IMAGE); - message.setRelativeFilePath(message.getUuid()+"."+extension); - } else { - message.setType(Message.TYPE_FILE); - } - message.setEncryption(Message.ENCRYPTION_PGP); - } - } else { - message.setType(Message.TYPE_FILE); - } - if (message.getType() == Message.TYPE_FILE) { - String suffix = ""; - if (!fileNameElement.getContent().isEmpty()) { - String parts[] = fileNameElement.getContent().split("/"); - suffix = parts[parts.length - 1]; - if (message.getEncryption() == Message.ENCRYPTION_PGP && (suffix.endsWith(".pgp") || suffix.endsWith(".gpg"))) { - suffix = suffix.substring(0,suffix.length() - 4); - } - } - message.setRelativeFilePath(message.getUuid()+"_"+suffix); - } - long size = Long.parseLong(fileSize.getContent()); - message.setBody(Long.toString(size)); - conversation.add(message); - mJingleConnectionManager.updateConversationUi(true); - if (mJingleConnectionManager.hasStoragePermission() - && size < this.mJingleConnectionManager.getAutoAcceptFileSize() - && mXmppConnectionService.isDataSaverDisabled()) { - Log.d(Config.LOGTAG, "auto accepting file from "+ packet.getFrom()); - this.acceptedAutomatically = true; - this.sendAccept(); - } else { - message.markUnread(); - Log.d(Config.LOGTAG, - "not auto accepting new file offer with size: " - + size - + " allowed size:" - + this.mJingleConnectionManager - .getAutoAcceptFileSize()); - this.mXmppConnectionService.getNotificationService().push(message); - } - this.file = this.mXmppConnectionService.getFileBackend().getFile(message, false); - if (mXmppAxolotlMessage != null) { - XmppAxolotlMessage.XmppAxolotlKeyTransportMessage transportMessage = account.getAxolotlService().processReceivingKeyTransportMessage(mXmppAxolotlMessage, false); - if (transportMessage != null) { - message.setEncryption(Message.ENCRYPTION_AXOLOTL); - this.file.setKey(transportMessage.getKey()); - this.file.setIv(transportMessage.getIv()); - message.setFingerprint(transportMessage.getFingerprint()); - } else { - Log.d(Config.LOGTAG,"could not process KeyTransportMessage"); - } - } - this.file.setExpectedSize(size); - message.resetFileParams(); - Log.d(Config.LOGTAG, "receiving file: expecting size of " + this.file.getExpectedSize()); - } else { - this.sendCancel(); - this.fail(); - } - } else { - this.sendCancel(); - this.fail(); - } - } + if (this.initialTransport == Transport.SOCKS) { + this.mergeCandidates(JingleCandidate.parse(content.socks5transport().getChildren())); + } else if (this.initialTransport == Transport.IBB) { + final String receivedBlockSize = content.ibbTransport().getAttribute("block-size"); + if (receivedBlockSize != null) { + try { + this.ibbBlockSize = Math.min(Integer.parseInt(receivedBlockSize), this.ibbBlockSize); + } catch (NumberFormatException e) { + Log.d(Config.LOGTAG, "number format exception " + e.getMessage()); + this.sendCancel(); + this.fail(); + return; + } + } else { + Log.d(Config.LOGTAG, "received block size was null"); + this.sendCancel(); + this.fail(); + return; + } + } + this.ftVersion = content.getVersion(); + if (ftVersion == null) { + this.sendCancel(); + this.fail(); + return; + } + this.fileOffer = content.getFileOffer(this.ftVersion); - private void sendInitRequest() { - JinglePacket packet = this.bootstrapPacket("session-initiate"); - Content content = new Content(this.contentCreator, this.contentName); - if (message.isFileOrImage()) { - content.setTransportId(this.transportId); - this.file = this.mXmppConnectionService.getFileBackend().getFile(message, false); - if (message.getEncryption() == Message.ENCRYPTION_AXOLOTL) { - this.file.setKey(mXmppAxolotlMessage.getInnerKey()); - this.file.setIv(mXmppAxolotlMessage.getIV()); - this.file.setExpectedSize(file.getSize() + 16); - content.setFileOffer(this.file, false, this.ftVersion).addChild(mXmppAxolotlMessage.toElement()); - } else { - this.file.setExpectedSize(file.getSize()); - content.setFileOffer(this.file, false, this.ftVersion); - } - message.resetFileParams(); - try { - this.mFileInputStream = new FileInputStream(file); - } catch (FileNotFoundException e) { - abort(); - return; - } - content.setTransportId(this.transportId); - content.socks5transport().setChildren(getCandidatesAsElements()); - packet.setContent(content); - this.sendJinglePacket(packet,new OnIqPacketReceived() { + if (fileOffer != null) { + Element encrypted = fileOffer.findChild("encrypted", AxolotlService.PEP_PREFIX); + if (encrypted != null) { + this.mXmppAxolotlMessage = XmppAxolotlMessage.fromElement(encrypted, packet.getFrom().asBareJid()); + } + Element fileSize = fileOffer.findChild("size"); + Element fileNameElement = fileOffer.findChild("name"); + if (fileNameElement != null) { + String[] filename = fileNameElement.getContent() + .toLowerCase(Locale.US).toLowerCase().split("\\."); + String extension = filename[filename.length - 1]; + if (VALID_IMAGE_EXTENSIONS.contains(extension)) { + message.setType(Message.TYPE_IMAGE); + message.setRelativeFilePath(message.getUuid() + "." + extension); + } else if (VALID_CRYPTO_EXTENSIONS.contains( + filename[filename.length - 1])) { + if (filename.length == 3) { + extension = filename[filename.length - 2]; + if (VALID_IMAGE_EXTENSIONS.contains(extension)) { + message.setType(Message.TYPE_IMAGE); + message.setRelativeFilePath(message.getUuid() + "." + extension); + } else { + message.setType(Message.TYPE_FILE); + } + message.setEncryption(Message.ENCRYPTION_PGP); + } + } else { + message.setType(Message.TYPE_FILE); + } + if (message.getType() == Message.TYPE_FILE) { + String suffix = ""; + if (!fileNameElement.getContent().isEmpty()) { + String parts[] = fileNameElement.getContent().split("/"); + suffix = parts[parts.length - 1]; + if (message.getEncryption() == Message.ENCRYPTION_PGP && (suffix.endsWith(".pgp") || suffix.endsWith(".gpg"))) { + suffix = suffix.substring(0, suffix.length() - 4); + } + } + message.setRelativeFilePath(message.getUuid() + "_" + suffix); + } + long size = Long.parseLong(fileSize.getContent()); + message.setBody(Long.toString(size)); + conversation.add(message); + mJingleConnectionManager.updateConversationUi(true); + this.file = this.mXmppConnectionService.getFileBackend().getFile(message, false); + if (mXmppAxolotlMessage != null) { + XmppAxolotlMessage.XmppAxolotlKeyTransportMessage transportMessage = account.getAxolotlService().processReceivingKeyTransportMessage(mXmppAxolotlMessage, false); + if (transportMessage != null) { + message.setEncryption(Message.ENCRYPTION_AXOLOTL); + this.file.setKey(transportMessage.getKey()); + this.file.setIv(transportMessage.getIv()); + message.setFingerprint(transportMessage.getFingerprint()); + } else { + Log.d(Config.LOGTAG, "could not process KeyTransportMessage"); + } + } + message.resetFileParams(); + this.file.setExpectedSize(size); + if (mJingleConnectionManager.hasStoragePermission() + && size < this.mJingleConnectionManager.getAutoAcceptFileSize() + && mXmppConnectionService.isDataSaverDisabled()) { + Log.d(Config.LOGTAG, "auto accepting file from " + packet.getFrom()); + this.acceptedAutomatically = true; + this.sendAccept(); + } else { + message.markUnread(); + Log.d(Config.LOGTAG, + "not auto accepting new file offer with size: " + + size + + " allowed size:" + + this.mJingleConnectionManager + .getAutoAcceptFileSize()); + this.mXmppConnectionService.getNotificationService().push(message); + } + Log.d(Config.LOGTAG, "receiving file: expecting size of " + this.file.getExpectedSize()); + } else { + this.sendCancel(); + this.fail(); + } + } else { + this.sendCancel(); + this.fail(); + } + } - @Override - public void onIqPacketReceived(Account account, IqPacket packet) { - if (packet.getType() == IqPacket.TYPE.RESULT) { - Log.d(Config.LOGTAG,account.getJid().asBareJid()+": other party received offer"); - if (mJingleStatus == JINGLE_STATUS_OFFERED) { - mJingleStatus = JINGLE_STATUS_INITIATED; - mXmppConnectionService.markMessage(message, Message.STATUS_OFFERED); - } else { - Log.d(Config.LOGTAG,"received ack for offer when status was "+mJingleStatus); - } - } else { - fail(IqParser.extractErrorMessage(packet)); - } - } - }); + private void sendInitRequest() { + JinglePacket packet = this.bootstrapPacket("session-initiate"); + Content content = new Content(this.contentCreator, this.contentName); + if (message.isFileOrImage()) { + content.setTransportId(this.transportId); + this.file = this.mXmppConnectionService.getFileBackend().getFile(message, false); + if (message.getEncryption() == Message.ENCRYPTION_AXOLOTL) { + this.file.setKey(mXmppAxolotlMessage.getInnerKey()); + this.file.setIv(mXmppAxolotlMessage.getIV()); + this.file.setExpectedSize(file.getSize() + 16); + content.setFileOffer(this.file, false, this.ftVersion).addChild(mXmppAxolotlMessage.toElement()); + } else { + this.file.setExpectedSize(file.getSize()); + content.setFileOffer(this.file, false, this.ftVersion); + } + message.resetFileParams(); + try { + this.mFileInputStream = new FileInputStream(file); + } catch (FileNotFoundException e) { + abort(); + return; + } + content.setTransportId(this.transportId); + if (this.initialTransport == Transport.IBB) { + content.ibbTransport().setAttribute("block-size", Integer.toString(this.ibbBlockSize)); + } else { + content.socks5transport().setChildren(getCandidatesAsElements()); + } + packet.setContent(content); + this.sendJinglePacket(packet, (account, response) -> { + if (response.getType() == IqPacket.TYPE.RESULT) { + Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": other party received offer"); + if (mJingleStatus == JINGLE_STATUS_OFFERED) { + mJingleStatus = JINGLE_STATUS_INITIATED; + mXmppConnectionService.markMessage(message, Message.STATUS_OFFERED); + } else { + Log.d(Config.LOGTAG, "received ack for offer when status was " + mJingleStatus); + } + } else { + fail(IqParser.extractErrorMessage(response)); + } + }); - } - } + } + } - private void sendHash() { - JinglePacket packet = this.bootstrapPacket("session-info"); - packet.addChecksum(file.getSha1Sum(),ftVersion.getNamespace()); - this.sendJinglePacket(packet); - } + private void sendHash() { + JinglePacket packet = this.bootstrapPacket("session-info"); + packet.addChecksum(file.getSha1Sum(), ftVersion.getNamespace()); + this.sendJinglePacket(packet); + } - private List getCandidatesAsElements() { - List elements = new ArrayList<>(); - for (JingleCandidate c : this.candidates) { - if (c.isOurs()) { - elements.add(c.toElement()); - } - } - return elements; - } + private List getCandidatesAsElements() { + List elements = new ArrayList<>(); + for (JingleCandidate c : this.candidates) { + if (c.isOurs()) { + elements.add(c.toElement()); + } + } + return elements; + } - private void sendAccept() { - mJingleStatus = JINGLE_STATUS_ACCEPTED; - this.mStatus = Transferable.STATUS_DOWNLOADING; - this.mJingleConnectionManager.updateConversationUi(true); - this.mJingleConnectionManager.getPrimaryCandidate(this.account, new OnPrimaryCandidateFound() { - @Override - public void onPrimaryCandidateFound(boolean success, final JingleCandidate candidate) { - final JinglePacket packet = bootstrapPacket("session-accept"); - final Content content = new Content(contentCreator,contentName); - content.setFileOffer(fileOffer, ftVersion); - content.setTransportId(transportId); - if (success && candidate != null && !equalCandidateExists(candidate)) { - final JingleSocks5Transport socksConnection = new JingleSocks5Transport( - JingleConnection.this, - candidate); - connections.put(candidate.getCid(), socksConnection); - socksConnection.connect(new OnTransportConnected() { + private void sendAccept() { + mJingleStatus = JINGLE_STATUS_ACCEPTED; + this.mStatus = Transferable.STATUS_DOWNLOADING; + this.mJingleConnectionManager.updateConversationUi(true); + if (initialTransport == Transport.SOCKS) { + sendAcceptSocks(); + } else { + sendAcceptIbb(); + } + } - @Override - public void failed() { - Log.d(Config.LOGTAG,"connection to our own primary candidate failed"); - content.socks5transport().setChildren(getCandidatesAsElements()); - packet.setContent(content); - sendJinglePacket(packet); - connectNextCandidate(); - } + private void sendAcceptSocks() { + this.mJingleConnectionManager.getPrimaryCandidate(this.account, (success, candidate) -> { + final JinglePacket packet = bootstrapPacket("session-accept"); + final Content content = new Content(contentCreator, contentName); + content.setFileOffer(fileOffer, ftVersion); + content.setTransportId(transportId); + if (success && candidate != null && !equalCandidateExists(candidate)) { + final JingleSocks5Transport socksConnection = new JingleSocks5Transport(this, candidate); + connections.put(candidate.getCid(), socksConnection); + socksConnection.connect(new OnTransportConnected() { - @Override - public void established() { - Log.d(Config.LOGTAG, "connected to primary candidate"); - mergeCandidate(candidate); - content.socks5transport().setChildren(getCandidatesAsElements()); - packet.setContent(content); - sendJinglePacket(packet); - connectNextCandidate(); - } - }); - } else { - Log.d(Config.LOGTAG,"did not find a primary candidate for ourself"); - content.socks5transport().setChildren(getCandidatesAsElements()); - packet.setContent(content); - sendJinglePacket(packet); - connectNextCandidate(); - } - } - }); - } + @Override + public void failed() { + Log.d(Config.LOGTAG, "connection to our own primary candidate failed"); + content.socks5transport().setChildren(getCandidatesAsElements()); + packet.setContent(content); + sendJinglePacket(packet); + connectNextCandidate(); + } - private JinglePacket bootstrapPacket(String action) { - JinglePacket packet = new JinglePacket(); - packet.setAction(action); - packet.setFrom(account.getJid()); - packet.setTo(this.message.getCounterpart()); - packet.setSessionId(this.sessionId); - packet.setInitiator(this.initiator); - return packet; - } + @Override + public void established() { + Log.d(Config.LOGTAG, "connected to primary candidate"); + mergeCandidate(candidate); + content.socks5transport().setChildren(getCandidatesAsElements()); + packet.setContent(content); + sendJinglePacket(packet); + connectNextCandidate(); + } + }); + } else { + Log.d(Config.LOGTAG, "did not find a primary candidate for ourself"); + content.socks5transport().setChildren(getCandidatesAsElements()); + packet.setContent(content); + sendJinglePacket(packet); + connectNextCandidate(); + } + }); + } - private void sendJinglePacket(JinglePacket packet) { - mXmppConnectionService.sendIqPacket(account,packet,responseListener); - } + private void sendAcceptIbb() { + this.transport = new JingleInbandTransport(this, this.transportId, this.ibbBlockSize); + final JinglePacket packet = bootstrapPacket("session-accept"); + final Content content = new Content(contentCreator, contentName); + content.setFileOffer(fileOffer, ftVersion); + content.setTransportId(transportId); + content.ibbTransport().setAttribute("block-size", this.ibbBlockSize); + packet.setContent(content); + this.transport.receive(file, onFileTransmissionStatusChanged); + this.sendJinglePacket(packet); + } - private void sendJinglePacket(JinglePacket packet, OnIqPacketReceived callback) { - mXmppConnectionService.sendIqPacket(account,packet,callback); - } + private JinglePacket bootstrapPacket(String action) { + JinglePacket packet = new JinglePacket(); + packet.setAction(action); + packet.setFrom(account.getJid()); + packet.setTo(this.message.getCounterpart()); + packet.setSessionId(this.sessionId); + packet.setInitiator(this.initiator); + return packet; + } - private boolean receiveAccept(JinglePacket packet) { - Content content = packet.getJingleContent(); - mergeCandidates(JingleCandidate.parse(content.socks5transport() - .getChildren())); - this.mJingleStatus = JINGLE_STATUS_ACCEPTED; - mXmppConnectionService.markMessage(message, Message.STATUS_UNSEND); - this.connectNextCandidate(); - return true; - } + private void sendJinglePacket(JinglePacket packet) { + mXmppConnectionService.sendIqPacket(account, packet, responseListener); + } - private boolean receiveTransportInfo(JinglePacket packet) { - Content content = packet.getJingleContent(); - if (content.hasSocks5Transport()) { - if (content.socks5transport().hasChild("activated")) { - if ((this.transport != null) && (this.transport instanceof JingleSocks5Transport)) { - onProxyActivated.success(); - } else { - String cid = content.socks5transport().findChild("activated").getAttribute("cid"); - Log.d(Config.LOGTAG, "received proxy activated (" + cid - + ")prior to choosing our own transport"); - JingleSocks5Transport connection = this.connections.get(cid); - if (connection != null) { - connection.setActivated(true); - } else { - Log.d(Config.LOGTAG, "activated connection not found"); - this.sendCancel(); - this.fail(); - } - } - return true; - } else if (content.socks5transport().hasChild("proxy-error")) { - onProxyActivated.failed(); - return true; - } else if (content.socks5transport().hasChild("candidate-error")) { - Log.d(Config.LOGTAG, "received candidate error"); - this.receivedCandidate = true; - if (mJingleStatus == JINGLE_STATUS_ACCEPTED && this.sentCandidate) { - this.connect(); - } - return true; - } else if (content.socks5transport().hasChild("candidate-used")) { - String cid = content.socks5transport() - .findChild("candidate-used").getAttribute("cid"); - if (cid != null) { - Log.d(Config.LOGTAG, "candidate used by counterpart:" + cid); - JingleCandidate candidate = getCandidate(cid); - if (candidate == null) { - Log.d(Config.LOGTAG,"could not find candidate with cid="+cid); - return false; - } - candidate.flagAsUsedByCounterpart(); - this.receivedCandidate = true; - if (mJingleStatus == JINGLE_STATUS_ACCEPTED && this.sentCandidate) { - this.connect(); - } else { - Log.d(Config.LOGTAG, "ignoring because file is already in transmission or we haven't sent our candidate yet status="+mJingleStatus+" sentCandidate="+Boolean.toString(sentCandidate)); - } - return true; - } else { - return false; - } - } else { - return false; - } - } else { - return true; - } - } + private void sendJinglePacket(JinglePacket packet, OnIqPacketReceived callback) { + mXmppConnectionService.sendIqPacket(account, packet, callback); + } - private void connect() { - final JingleSocks5Transport connection = chooseConnection(); - this.transport = connection; - if (connection == null) { - Log.d(Config.LOGTAG, "could not find suitable candidate"); - this.disconnectSocks5Connections(); - if (initiating()) { - this.sendFallbackToIbb(); - } - } else { - this.mJingleStatus = JINGLE_STATUS_TRANSMITTING; - if (connection.needsActivation()) { - if (connection.getCandidate().isOurs()) { - final String sid; - if (ftVersion == Content.Version.FT_3) { - Log.d(Config.LOGTAG,account.getJid().asBareJid()+": use session ID instead of transport ID to activate proxy"); - sid = getSessionId(); - } else { - sid = getTransportId(); - } - Log.d(Config.LOGTAG, "candidate " - + connection.getCandidate().getCid() - + " was our proxy. going to activate"); - IqPacket activation = new IqPacket(IqPacket.TYPE.SET); - activation.setTo(connection.getCandidate().getJid()); - activation.query("http://jabber.org/protocol/bytestreams") - .setAttribute("sid", sid); - activation.query().addChild("activate") - .setContent(this.getCounterPart().toString()); - mXmppConnectionService.sendIqPacket(account,activation, - new OnIqPacketReceived() { + private boolean receiveAccept(JinglePacket packet) { + if (this.mJingleStatus != JINGLE_STATUS_INITIATED) { + Log.d(Config.LOGTAG,account.getJid().asBareJid()+": received out of order session-accept"); + return false; + } + this.mJingleStatus = JINGLE_STATUS_ACCEPTED; + mXmppConnectionService.markMessage(message, Message.STATUS_UNSEND); + Content content = packet.getJingleContent(); + if (content.hasSocks5Transport()) { + mergeCandidates(JingleCandidate.parse(content.socks5transport().getChildren())); + this.connectNextCandidate(); + return true; + } else if (content.hasIbbTransport()) { + String receivedBlockSize = packet.getJingleContent().ibbTransport().getAttribute("block-size"); + if (receivedBlockSize != null) { + int bs = Integer.parseInt(receivedBlockSize); + if (bs > this.ibbBlockSize) { + this.ibbBlockSize = bs; + } + } + this.transport = new JingleInbandTransport(this, this.transportId, this.ibbBlockSize); + this.transport.connect(onIbbTransportConnected); + return true; + } else { + return false; + } + } - @Override - public void onIqPacketReceived(Account account, - IqPacket packet) { - if (packet.getType() != IqPacket.TYPE.RESULT) { - onProxyActivated.failed(); - } else { - onProxyActivated.success(); - sendProxyActivated(connection.getCandidate().getCid()); - } - } - }); - } else { - Log.d(Config.LOGTAG, - "candidate " - + connection.getCandidate().getCid() - + " was a proxy. waiting for other party to activate"); - } - } else { - if (initiating()) { - Log.d(Config.LOGTAG, "we were initiating. sending file"); - connection.send(file, onFileTransmissionStatusChanged); - } else { - Log.d(Config.LOGTAG, "we were responding. receiving file"); - connection.receive(file, onFileTransmissionStatusChanged); - } - } - } - } + private boolean receiveTransportInfo(JinglePacket packet) { + Content content = packet.getJingleContent(); + if (content.hasSocks5Transport()) { + if (content.socks5transport().hasChild("activated")) { + if ((this.transport != null) && (this.transport instanceof JingleSocks5Transport)) { + onProxyActivated.success(); + } else { + String cid = content.socks5transport().findChild("activated").getAttribute("cid"); + Log.d(Config.LOGTAG, "received proxy activated (" + cid + + ")prior to choosing our own transport"); + JingleSocks5Transport connection = this.connections.get(cid); + if (connection != null) { + connection.setActivated(true); + } else { + Log.d(Config.LOGTAG, "activated connection not found"); + this.sendCancel(); + this.fail(); + } + } + return true; + } else if (content.socks5transport().hasChild("proxy-error")) { + onProxyActivated.failed(); + return true; + } else if (content.socks5transport().hasChild("candidate-error")) { + Log.d(Config.LOGTAG, "received candidate error"); + this.receivedCandidate = true; + if (mJingleStatus == JINGLE_STATUS_ACCEPTED && this.sentCandidate) { + this.connect(); + } + return true; + } else if (content.socks5transport().hasChild("candidate-used")) { + String cid = content.socks5transport().findChild("candidate-used").getAttribute("cid"); + if (cid != null) { + Log.d(Config.LOGTAG, "candidate used by counterpart:" + cid); + JingleCandidate candidate = getCandidate(cid); + if (candidate == null) { + Log.d(Config.LOGTAG, "could not find candidate with cid=" + cid); + return false; + } + candidate.flagAsUsedByCounterpart(); + this.receivedCandidate = true; + if (mJingleStatus == JINGLE_STATUS_ACCEPTED && this.sentCandidate) { + this.connect(); + } else { + Log.d(Config.LOGTAG, "ignoring because file is already in transmission or we haven't sent our candidate yet status=" + mJingleStatus + " sentCandidate=" + sentCandidate); + } + return true; + } else { + return false; + } + } else { + return false; + } + } else { + return true; + } + } - private JingleSocks5Transport chooseConnection() { - JingleSocks5Transport connection = null; - for (Entry cursor : connections - .entrySet()) { - JingleSocks5Transport currentConnection = cursor.getValue(); - // Log.d(Config.LOGTAG,"comparing candidate: "+currentConnection.getCandidate().toString()); - if (currentConnection.isEstablished() - && (currentConnection.getCandidate().isUsedByCounterpart() || (!currentConnection - .getCandidate().isOurs()))) { - // Log.d(Config.LOGTAG,"is usable"); - if (connection == null) { - connection = currentConnection; - } else { - if (connection.getCandidate().getPriority() < currentConnection - .getCandidate().getPriority()) { - connection = currentConnection; - } else if (connection.getCandidate().getPriority() == currentConnection - .getCandidate().getPriority()) { - // Log.d(Config.LOGTAG,"found two candidates with same priority"); - if (initiating()) { - if (currentConnection.getCandidate().isOurs()) { - connection = currentConnection; - } - } else { - if (!currentConnection.getCandidate().isOurs()) { - connection = currentConnection; - } - } - } - } - } - } - return connection; - } + private void connect() { + final JingleSocks5Transport connection = chooseConnection(); + this.transport = connection; + if (connection == null) { + Log.d(Config.LOGTAG, "could not find suitable candidate"); + this.disconnectSocks5Connections(); + if (initiating()) { + this.sendFallbackToIbb(); + } + } else { + this.mJingleStatus = JINGLE_STATUS_TRANSMITTING; + if (connection.needsActivation()) { + if (connection.getCandidate().isOurs()) { + final String sid; + if (ftVersion == Content.Version.FT_3) { + Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": use session ID instead of transport ID to activate proxy"); + sid = getSessionId(); + } else { + sid = getTransportId(); + } + Log.d(Config.LOGTAG, "candidate " + + connection.getCandidate().getCid() + + " was our proxy. going to activate"); + IqPacket activation = new IqPacket(IqPacket.TYPE.SET); + activation.setTo(connection.getCandidate().getJid()); + activation.query("http://jabber.org/protocol/bytestreams") + .setAttribute("sid", sid); + activation.query().addChild("activate") + .setContent(this.getCounterPart().toString()); + mXmppConnectionService.sendIqPacket(account, activation, (account, response) -> { + if (response.getType() != IqPacket.TYPE.RESULT) { + onProxyActivated.failed(); + } else { + onProxyActivated.success(); + sendProxyActivated(connection.getCandidate().getCid()); + } + }); + } else { + Log.d(Config.LOGTAG, + "candidate " + + connection.getCandidate().getCid() + + " was a proxy. waiting for other party to activate"); + } + } else { + if (initiating()) { + Log.d(Config.LOGTAG, "we were initiating. sending file"); + connection.send(file, onFileTransmissionStatusChanged); + } else { + Log.d(Config.LOGTAG, "we were responding. receiving file"); + connection.receive(file, onFileTransmissionStatusChanged); + } + } + } + } - private void sendSuccess() { - JinglePacket packet = bootstrapPacket("session-terminate"); - Reason reason = new Reason(); - reason.addChild("success"); - packet.setReason(reason); - this.sendJinglePacket(packet); - this.disconnectSocks5Connections(); - this.mJingleStatus = JINGLE_STATUS_FINISHED; - this.message.setStatus(Message.STATUS_RECEIVED); - this.message.setTransferable(null); - this.mXmppConnectionService.updateMessage(message, false); - this.mJingleConnectionManager.finishConnection(this); - } + private JingleSocks5Transport chooseConnection() { + JingleSocks5Transport connection = null; + for (Entry cursor : connections + .entrySet()) { + JingleSocks5Transport currentConnection = cursor.getValue(); + // Log.d(Config.LOGTAG,"comparing candidate: "+currentConnection.getCandidate().toString()); + if (currentConnection.isEstablished() + && (currentConnection.getCandidate().isUsedByCounterpart() || (!currentConnection + .getCandidate().isOurs()))) { + // Log.d(Config.LOGTAG,"is usable"); + if (connection == null) { + connection = currentConnection; + } else { + if (connection.getCandidate().getPriority() < currentConnection + .getCandidate().getPriority()) { + connection = currentConnection; + } else if (connection.getCandidate().getPriority() == currentConnection + .getCandidate().getPriority()) { + // Log.d(Config.LOGTAG,"found two candidates with same priority"); + if (initiating()) { + if (currentConnection.getCandidate().isOurs()) { + connection = currentConnection; + } + } else { + if (!currentConnection.getCandidate().isOurs()) { + connection = currentConnection; + } + } + } + } + } + } + return connection; + } - private void sendFallbackToIbb() { - Log.d(Config.LOGTAG, account.getJid().asBareJid()+": sending fallback to ibb"); - JinglePacket packet = this.bootstrapPacket("transport-replace"); - Content content = new Content(this.contentCreator, this.contentName); - this.transportId = this.mJingleConnectionManager.nextRandomId(); - content.setTransportId(this.transportId); - content.ibbTransport().setAttribute("block-size", - Integer.toString(this.ibbBlockSize)); - packet.setContent(content); - this.sendJinglePacket(packet); - } + private void sendSuccess() { + JinglePacket packet = bootstrapPacket("session-terminate"); + Reason reason = new Reason(); + reason.addChild("success"); + packet.setReason(reason); + this.sendJinglePacket(packet); + this.disconnectSocks5Connections(); + this.mJingleStatus = JINGLE_STATUS_FINISHED; + this.message.setStatus(Message.STATUS_RECEIVED); + this.message.setTransferable(null); + this.mXmppConnectionService.updateMessage(message, false); + this.mJingleConnectionManager.finishConnection(this); + } - OnTransportConnected onIbbTransportConnected = new OnTransportConnected() { - @Override - public void failed() { - Log.d(Config.LOGTAG, "ibb open failed"); - } - - @Override - public void established() { - JingleConnection.this.transport.send(file, onFileTransmissionStatusChanged); - } - }; - - private boolean receiveFallbackToIbb(JinglePacket packet) { - Log.d(Config.LOGTAG, "receiving fallack to ibb"); - String receivedBlockSize = packet.getJingleContent().ibbTransport() - .getAttribute("block-size"); - if (receivedBlockSize != null) { - int bs = Integer.parseInt(receivedBlockSize); - if (bs > this.ibbBlockSize) { - this.ibbBlockSize = bs; - } - } - this.transportId = packet.getJingleContent().getTransportId(); - this.transport = new JingleInbandTransport(this, this.transportId, this.ibbBlockSize); - - JinglePacket answer = bootstrapPacket("transport-accept"); - Content content = new Content("initiator", "a-file-offer"); - content.setTransportId(this.transportId); - content.ibbTransport().setAttribute("block-size",this.ibbBlockSize); - answer.setContent(content); + private void sendFallbackToIbb() { + Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": sending fallback to ibb"); + JinglePacket packet = this.bootstrapPacket("transport-replace"); + Content content = new Content(this.contentCreator, this.contentName); + this.transportId = this.mJingleConnectionManager.nextRandomId(); + content.setTransportId(this.transportId); + content.ibbTransport().setAttribute("block-size", + Integer.toString(this.ibbBlockSize)); + packet.setContent(content); + this.sendJinglePacket(packet); + } - if (initiating()) { - this.sendJinglePacket(answer, new OnIqPacketReceived() { - @Override - public void onIqPacketReceived(Account account, IqPacket packet) { - if (packet.getType() == IqPacket.TYPE.RESULT) { - Log.d(Config.LOGTAG, account.getJid().asBareJid() + " recipient ACKed our transport-accept. creating ibb"); - transport.connect(onIbbTransportConnected); - } - } - }); - } else { - this.transport.receive(file, onFileTransmissionStatusChanged); - this.sendJinglePacket(answer); - } - return true; - } + private boolean receiveFallbackToIbb(JinglePacket packet) { + Log.d(Config.LOGTAG, "receiving fallack to ibb"); + String receivedBlockSize = packet.getJingleContent().ibbTransport() + .getAttribute("block-size"); + if (receivedBlockSize != null) { + int bs = Integer.parseInt(receivedBlockSize); + if (bs > this.ibbBlockSize) { + this.ibbBlockSize = bs; + } + } + this.transportId = packet.getJingleContent().getTransportId(); + this.transport = new JingleInbandTransport(this, this.transportId, this.ibbBlockSize); - private boolean receiveTransportAccept(JinglePacket packet) { - if (packet.getJingleContent().hasIbbTransport()) { - String receivedBlockSize = packet.getJingleContent().ibbTransport() - .getAttribute("block-size"); - if (receivedBlockSize != null) { - int bs = Integer.parseInt(receivedBlockSize); - if (bs > this.ibbBlockSize) { - this.ibbBlockSize = bs; - } - } - this.transport = new JingleInbandTransport(this, this.transportId, this.ibbBlockSize); + JinglePacket answer = bootstrapPacket("transport-accept"); - //might be receive instead if we are not initiating - if (initiating()) { - this.transport.connect(onIbbTransportConnected); - } else { - this.transport.receive(file, onFileTransmissionStatusChanged); - } - return true; - } else { - return false; - } - } + final Content content = new Content(contentCreator, contentName); + content.setFileOffer(fileOffer, ftVersion); + content.ibbTransport().setAttribute("block-size", this.ibbBlockSize); + answer.setContent(content); - private void receiveSuccess() { - if (initiating()) { - this.mJingleStatus = JINGLE_STATUS_FINISHED; - this.mXmppConnectionService.markMessage(this.message, Message.STATUS_SEND_RECEIVED); - this.disconnectSocks5Connections(); - if (this.transport instanceof JingleInbandTransport) { - this.transport.disconnect(); - } - this.message.setTransferable(null); - this.mJingleConnectionManager.finishConnection(this); - } else { - Log.d(Config.LOGTAG,account.getJid().asBareJid()+": received session-terminate/success while responding"); - } - } - @Override - public void cancel() { - this.cancelled = true; - abort(); - } - public void abort() { - this.disconnectSocks5Connections(); - if (this.transport instanceof JingleInbandTransport) { - this.transport.disconnect(); - } - this.sendCancel(); - this.mJingleConnectionManager.finishConnection(this); - if (responding()) { - this.message.setTransferable(new TransferablePlaceholder(Transferable.STATUS_FAILED)); - if (this.file!=null) { - file.delete(); - } - this.mJingleConnectionManager.updateConversationUi(true); - } else { - this.mXmppConnectionService.markMessage(this.message, Message.STATUS_SEND_FAILED, cancelled ? Message.ERROR_MESSAGE_CANCELLED : null); - this.message.setTransferable(null); - } - } + if (initiating()) { + this.sendJinglePacket(answer, (account, response) -> { + if (response.getType() == IqPacket.TYPE.RESULT) { + Log.d(Config.LOGTAG, account.getJid().asBareJid() + " recipient ACKed our transport-accept. creating ibb"); + transport.connect(onIbbTransportConnected); + } + }); + } else { + this.transport.receive(file, onFileTransmissionStatusChanged); + this.sendJinglePacket(answer); + } + return true; + } - private void fail() { - fail(null); - } + private boolean receiveTransportAccept(JinglePacket packet) { + if (packet.getJingleContent().hasIbbTransport()) { + String receivedBlockSize = packet.getJingleContent().ibbTransport() + .getAttribute("block-size"); + if (receivedBlockSize != null) { + int bs = Integer.parseInt(receivedBlockSize); + if (bs > this.ibbBlockSize) { + this.ibbBlockSize = bs; + } + } + this.transport = new JingleInbandTransport(this, this.transportId, this.ibbBlockSize); - private void fail(String errorMessage) { - this.mJingleStatus = JINGLE_STATUS_FAILED; - this.disconnectSocks5Connections(); - if (this.transport instanceof JingleInbandTransport) { - this.transport.disconnect(); - } - FileBackend.close(mFileInputStream); - FileBackend.close(mFileOutputStream); - if (this.message != null) { - if (responding()) { - this.message.setTransferable(new TransferablePlaceholder(Transferable.STATUS_FAILED)); - if (this.file!=null) { - file.delete(); - } - this.mJingleConnectionManager.updateConversationUi(true); - } else { - this.mXmppConnectionService.markMessage(this.message, - Message.STATUS_SEND_FAILED, - cancelled ? Message.ERROR_MESSAGE_CANCELLED : errorMessage); - this.message.setTransferable(null); - } - } - this.mJingleConnectionManager.finishConnection(this); - } + //might be receive instead if we are not initiating + if (initiating()) { + this.transport.connect(onIbbTransportConnected); + } else { + this.transport.receive(file, onFileTransmissionStatusChanged); + } + return true; + } else { + return false; + } + } - private void sendCancel() { - JinglePacket packet = bootstrapPacket("session-terminate"); - Reason reason = new Reason(); - reason.addChild("cancel"); - packet.setReason(reason); - this.sendJinglePacket(packet); - } + private void receiveSuccess() { + if (initiating()) { + this.mJingleStatus = JINGLE_STATUS_FINISHED; + this.mXmppConnectionService.markMessage(this.message, Message.STATUS_SEND_RECEIVED); + this.disconnectSocks5Connections(); + if (this.transport instanceof JingleInbandTransport) { + this.transport.disconnect(); + } + this.message.setTransferable(null); + this.mJingleConnectionManager.finishConnection(this); + } else { + Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": received session-terminate/success while responding"); + } + } - private void connectNextCandidate() { - for (JingleCandidate candidate : this.candidates) { - if ((!connections.containsKey(candidate.getCid()) && (!candidate - .isOurs()))) { - this.connectWithCandidate(candidate); - return; - } - } - this.sendCandidateError(); - } + @Override + public void cancel() { + this.cancelled = true; + abort(); + } - private void connectWithCandidate(final JingleCandidate candidate) { - final JingleSocks5Transport socksConnection = new JingleSocks5Transport( - this, candidate); - connections.put(candidate.getCid(), socksConnection); - socksConnection.connect(new OnTransportConnected() { + public void abort() { + this.disconnectSocks5Connections(); + if (this.transport instanceof JingleInbandTransport) { + this.transport.disconnect(); + } + this.sendCancel(); + this.mJingleConnectionManager.finishConnection(this); + if (responding()) { + this.message.setTransferable(new TransferablePlaceholder(Transferable.STATUS_FAILED)); + if (this.file != null) { + file.delete(); + } + this.mJingleConnectionManager.updateConversationUi(true); + } else { + this.mXmppConnectionService.markMessage(this.message, Message.STATUS_SEND_FAILED, cancelled ? Message.ERROR_MESSAGE_CANCELLED : null); + this.message.setTransferable(null); + } + } - @Override - public void failed() { - Log.d(Config.LOGTAG, - "connection failed with " + candidate.getHost() + ":" - + candidate.getPort()); - connectNextCandidate(); - } + private void fail() { + fail(null); + } - @Override - public void established() { - Log.d(Config.LOGTAG, - "established connection with " + candidate.getHost() - + ":" + candidate.getPort()); - sendCandidateUsed(candidate.getCid()); - } - }); - } + private void fail(String errorMessage) { + this.mJingleStatus = JINGLE_STATUS_FAILED; + this.disconnectSocks5Connections(); + if (this.transport instanceof JingleInbandTransport) { + this.transport.disconnect(); + } + FileBackend.close(mFileInputStream); + FileBackend.close(mFileOutputStream); + if (this.message != null) { + if (responding()) { + this.message.setTransferable(new TransferablePlaceholder(Transferable.STATUS_FAILED)); + if (this.file != null) { + file.delete(); + } + this.mJingleConnectionManager.updateConversationUi(true); + } else { + this.mXmppConnectionService.markMessage(this.message, + Message.STATUS_SEND_FAILED, + cancelled ? Message.ERROR_MESSAGE_CANCELLED : errorMessage); + this.message.setTransferable(null); + } + } + this.mJingleConnectionManager.finishConnection(this); + } - private void disconnectSocks5Connections() { - Iterator> it = this.connections - .entrySet().iterator(); - while (it.hasNext()) { - Entry pairs = it.next(); - pairs.getValue().disconnect(); - it.remove(); - } - } + private void sendCancel() { + JinglePacket packet = bootstrapPacket("session-terminate"); + Reason reason = new Reason(); + reason.addChild("cancel"); + packet.setReason(reason); + this.sendJinglePacket(packet); + } - private void sendProxyActivated(String cid) { - JinglePacket packet = bootstrapPacket("transport-info"); - Content content = new Content(this.contentCreator, this.contentName); - content.setTransportId(this.transportId); - content.socks5transport().addChild("activated") - .setAttribute("cid", cid); - packet.setContent(content); - this.sendJinglePacket(packet); - } + private void connectNextCandidate() { + for (JingleCandidate candidate : this.candidates) { + if ((!connections.containsKey(candidate.getCid()) && (!candidate + .isOurs()))) { + this.connectWithCandidate(candidate); + return; + } + } + this.sendCandidateError(); + } - private void sendCandidateUsed(final String cid) { - JinglePacket packet = bootstrapPacket("transport-info"); - Content content = new Content(this.contentCreator, this.contentName); - content.setTransportId(this.transportId); - content.socks5transport().addChild("candidate-used").setAttribute("cid", cid); - packet.setContent(content); - this.sentCandidate = true; - if ((receivedCandidate) && (mJingleStatus == JINGLE_STATUS_ACCEPTED)) { - connect(); - } - this.sendJinglePacket(packet); - } + private void connectWithCandidate(final JingleCandidate candidate) { + final JingleSocks5Transport socksConnection = new JingleSocks5Transport( + this, candidate); + connections.put(candidate.getCid(), socksConnection); + socksConnection.connect(new OnTransportConnected() { - private void sendCandidateError() { - Log.d(Config.LOGTAG,"sending canditate error"); - JinglePacket packet = bootstrapPacket("transport-info"); - Content content = new Content(this.contentCreator, this.contentName); - content.setTransportId(this.transportId); - content.socks5transport().addChild("candidate-error"); - packet.setContent(content); - this.sentCandidate = true; - this.sendJinglePacket(packet); - if (receivedCandidate && mJingleStatus == JINGLE_STATUS_ACCEPTED) { - connect(); - } - } + @Override + public void failed() { + Log.d(Config.LOGTAG, + "connection failed with " + candidate.getHost() + ":" + + candidate.getPort()); + connectNextCandidate(); + } - public int getJingleStatus() { - return this.mJingleStatus; - } + @Override + public void established() { + Log.d(Config.LOGTAG, + "established connection with " + candidate.getHost() + + ":" + candidate.getPort()); + sendCandidateUsed(candidate.getCid()); + } + }); + } - private boolean equalCandidateExists(JingleCandidate candidate) { - for (JingleCandidate c : this.candidates) { - if (c.equalValues(candidate)) { - return true; - } - } - return false; - } + private void disconnectSocks5Connections() { + Iterator> it = this.connections + .entrySet().iterator(); + while (it.hasNext()) { + Entry pairs = it.next(); + pairs.getValue().disconnect(); + it.remove(); + } + } - private void mergeCandidate(JingleCandidate candidate) { - for (JingleCandidate c : this.candidates) { - if (c.equals(candidate)) { - return; - } - } - this.candidates.add(candidate); - } + private void sendProxyActivated(String cid) { + JinglePacket packet = bootstrapPacket("transport-info"); + Content content = new Content(this.contentCreator, this.contentName); + content.setTransportId(this.transportId); + content.socks5transport().addChild("activated").setAttribute("cid", cid); + packet.setContent(content); + this.sendJinglePacket(packet); + } - private void mergeCandidates(List candidates) { - for (JingleCandidate c : candidates) { - mergeCandidate(c); - } - } + private void sendCandidateUsed(final String cid) { + JinglePacket packet = bootstrapPacket("transport-info"); + Content content = new Content(this.contentCreator, this.contentName); + content.setTransportId(this.transportId); + content.socks5transport().addChild("candidate-used").setAttribute("cid", cid); + packet.setContent(content); + this.sentCandidate = true; + if ((receivedCandidate) && (mJingleStatus == JINGLE_STATUS_ACCEPTED)) { + connect(); + } + this.sendJinglePacket(packet); + } - private JingleCandidate getCandidate(String cid) { - for (JingleCandidate c : this.candidates) { - if (c.getCid().equals(cid)) { - return c; - } - } - return null; - } + private void sendCandidateError() { + Log.d(Config.LOGTAG, "sending candidate error"); + JinglePacket packet = bootstrapPacket("transport-info"); + Content content = new Content(this.contentCreator, this.contentName); + content.setTransportId(this.transportId); + content.socks5transport().addChild("candidate-error"); + packet.setContent(content); + this.sentCandidate = true; + this.sendJinglePacket(packet); + if (receivedCandidate && mJingleStatus == JINGLE_STATUS_ACCEPTED) { + connect(); + } + } - public void updateProgress(int i) { - this.mProgress = i; - mJingleConnectionManager.updateConversationUi(false); - } + public int getJingleStatus() { + return this.mJingleStatus; + } - public String getTransportId() { - return this.transportId; - } + private boolean equalCandidateExists(JingleCandidate candidate) { + for (JingleCandidate c : this.candidates) { + if (c.equalValues(candidate)) { + return true; + } + } + return false; + } - public Content.Version getFtVersion() { - return this.ftVersion; - } + private void mergeCandidate(JingleCandidate candidate) { + for (JingleCandidate c : this.candidates) { + if (c.equals(candidate)) { + return; + } + } + this.candidates.add(candidate); + } - interface OnProxyActivated { - void success(); + private void mergeCandidates(List candidates) { + for (JingleCandidate c : candidates) { + mergeCandidate(c); + } + } - void failed(); - } + private JingleCandidate getCandidate(String cid) { + for (JingleCandidate c : this.candidates) { + if (c.getCid().equals(cid)) { + return c; + } + } + return null; + } - public boolean hasTransportId(String sid) { - return sid.equals(this.transportId); - } + void updateProgress(int i) { + this.mProgress = i; + mJingleConnectionManager.updateConversationUi(false); + } - public JingleTransport getTransport() { - return this.transport; - } + public String getTransportId() { + return this.transportId; + } - public boolean start() { - if (account.getStatus() == Account.State.ONLINE) { - if (mJingleStatus == JINGLE_STATUS_INITIATED) { - new Thread(new Runnable() { + public Content.Version getFtVersion() { + return this.ftVersion; + } - @Override - public void run() { - sendAccept(); - } - }).start(); - } - return true; - } else { - return false; - } - } + public boolean hasTransportId(String sid) { + return sid.equals(this.transportId); + } - @Override - public int getStatus() { - return this.mStatus; - } + public JingleTransport getTransport() { + return this.transport; + } - @Override - public long getFileSize() { - if (this.file != null) { - return this.file.getExpectedSize(); - } else { - return 0; - } - } + public boolean start() { + if (account.getStatus() == Account.State.ONLINE) { + if (mJingleStatus == JINGLE_STATUS_INITIATED) { + new Thread(this::sendAccept).start(); + } + return true; + } else { + return false; + } + } - @Override - public int getProgress() { - return this.mProgress; - } + @Override + public int getStatus() { + return this.mStatus; + } - public AbstractConnectionManager getConnectionManager() { - return this.mJingleConnectionManager; - } + @Override + public long getFileSize() { + if (this.file != null) { + return this.file.getExpectedSize(); + } else { + return 0; + } + } + + @Override + public int getProgress() { + return this.mProgress; + } + + public AbstractConnectionManager getConnectionManager() { + return this.mJingleConnectionManager; + } + + interface OnProxyActivated { + void success(); + + void failed(); + } } diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleInbandTransport.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleInbandTransport.java index 54679631f..34bf5412e 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleInbandTransport.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleInbandTransport.java @@ -154,6 +154,7 @@ public class JingleInbandTransport extends JingleTransport { if (count == -1) { sendClose(); file.setSha1Sum(digest.digest()); + Log.d(Config.LOGTAG,account.getJid().asBareJid()+": sendNextBlock() count was -1"); this.onFileTransmissionStatusChanged.onFileTransmitted(file); fileInputStream.close(); return; @@ -181,6 +182,7 @@ public class JingleInbandTransport extends JingleTransport { } else { sendClose(); file.setSha1Sum(digest.digest()); + Log.d(Config.LOGTAG,account.getJid().asBareJid()+": sendNextBlock() remaining size"); this.onFileTransmissionStatusChanged.onFileTransmitted(file); fileInputStream.close(); } @@ -204,6 +206,7 @@ public class JingleInbandTransport extends JingleTransport { file.setSha1Sum(digest.digest()); fileOutputStream.flush(); fileOutputStream.close(); + Log.d(Config.LOGTAG,account.getJid().asBareJid()+": receive next block nothing remaining"); this.onFileTransmissionStatusChanged.onFileTransmitted(file); } else { connection.updateProgress((int) ((((double) (this.fileSize - this.remainingSize)) / this.fileSize) * 100)); diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/Transport.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/Transport.java new file mode 100644 index 000000000..4d0fb4b65 --- /dev/null +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/Transport.java @@ -0,0 +1,5 @@ +package eu.siacs.conversations.xmpp.jingle; + +public enum Transport { + SOCKS, IBB +} diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/stanzas/Content.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/stanzas/Content.java index 1b01cf0b3..3696756cf 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/stanzas/Content.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/stanzas/Content.java @@ -2,132 +2,129 @@ package eu.siacs.conversations.xmpp.jingle.stanzas; import eu.siacs.conversations.entities.DownloadableFile; import eu.siacs.conversations.xml.Element; +import eu.siacs.conversations.xml.Namespace; public class Content extends Element { - public enum Version { - FT_3("urn:xmpp:jingle:apps:file-transfer:3"), - FT_4("urn:xmpp:jingle:apps:file-transfer:4"), - FT_5("urn:xmpp:jingle:apps:file-transfer:5"); + public enum Version { + FT_3("urn:xmpp:jingle:apps:file-transfer:3"), + FT_4("urn:xmpp:jingle:apps:file-transfer:4"), + FT_5("urn:xmpp:jingle:apps:file-transfer:5"); - private final String namespace; + private final String namespace; - Version(String namespace) { - this.namespace = namespace; - } + Version(String namespace) { + this.namespace = namespace; + } - public String getNamespace() { - return namespace; - } - } + public String getNamespace() { + return namespace; + } + } - private String transportId; + private String transportId; - public Content() { - super("content"); - } + public Content() { + super("content"); + } - public Content(String creator, String name) { - super("content"); - this.setAttribute("creator", creator); - this.setAttribute("senders", creator); - this.setAttribute("name", name); - } + public Content(String creator, String name) { + super("content"); + this.setAttribute("creator", creator); + this.setAttribute("senders", creator); + this.setAttribute("name", name); + } - public Version getVersion() { - if (hasChild("description", Version.FT_3.namespace)) { - return Version.FT_3; - } else if (hasChild("description" , Version.FT_4.namespace)) { - return Version.FT_4; - } else if (hasChild("description" , Version.FT_5.namespace)) { - return Version.FT_5; - } - return null; - } + public Version getVersion() { + if (hasChild("description", Version.FT_3.namespace)) { + return Version.FT_3; + } else if (hasChild("description", Version.FT_4.namespace)) { + return Version.FT_4; + } else if (hasChild("description", Version.FT_5.namespace)) { + return Version.FT_5; + } + return null; + } - public void setTransportId(String sid) { - this.transportId = sid; - } + public void setTransportId(String sid) { + this.transportId = sid; + } - public Element setFileOffer(DownloadableFile actualFile, boolean otr, Version version) { - Element description = this.addChild("description", version.namespace); - Element file; - if (version == Version.FT_3) { - Element offer = description.addChild("offer"); - file = offer.addChild("file"); - } else { - file = description.addChild("file"); - } - file.addChild("size").setContent(Long.toString(actualFile.getExpectedSize())); - if (otr) { - file.addChild("name").setContent(actualFile.getName() + ".otr"); - } else { - file.addChild("name").setContent(actualFile.getName()); - } - return file; - } + public Element setFileOffer(DownloadableFile actualFile, boolean otr, Version version) { + Element description = this.addChild("description", version.namespace); + Element file; + if (version == Version.FT_3) { + Element offer = description.addChild("offer"); + file = offer.addChild("file"); + } else { + file = description.addChild("file"); + } + file.addChild("size").setContent(Long.toString(actualFile.getExpectedSize())); + if (otr) { + file.addChild("name").setContent(actualFile.getName() + ".otr"); + } else { + file.addChild("name").setContent(actualFile.getName()); + } + return file; + } - public Element getFileOffer(Version version) { - Element description = this.findChild("description", version.namespace); - if (description == null) { - return null; - } - if (version == Version.FT_3) { - Element offer = description.findChild("offer"); - if (offer == null) { - return null; - } - return offer.findChild("file"); - } else { - return description.findChild("file"); - } - } + public Element getFileOffer(Version version) { + Element description = this.findChild("description", version.namespace); + if (description == null) { + return null; + } + if (version == Version.FT_3) { + Element offer = description.findChild("offer"); + if (offer == null) { + return null; + } + return offer.findChild("file"); + } else { + return description.findChild("file"); + } + } - public void setFileOffer(Element fileOffer, Version version) { - Element description = this.addChild("description", version.namespace); - if (version == Version.FT_3) { - description.addChild("offer").addChild(fileOffer); - } else { - description.addChild(fileOffer); - } - } + public void setFileOffer(Element fileOffer, Version version) { + Element description = this.addChild("description", version.namespace); + if (version == Version.FT_3) { + description.addChild("offer").addChild(fileOffer); + } else { + description.addChild(fileOffer); + } + } - public String getTransportId() { - if (hasSocks5Transport()) { - this.transportId = socks5transport().getAttribute("sid"); - } else if (hasIbbTransport()) { - this.transportId = ibbTransport().getAttribute("sid"); - } - return this.transportId; - } + public String getTransportId() { + if (hasSocks5Transport()) { + this.transportId = socks5transport().getAttribute("sid"); + } else if (hasIbbTransport()) { + this.transportId = ibbTransport().getAttribute("sid"); + } + return this.transportId; + } - public Element socks5transport() { - Element transport = this.findChild("transport", - "urn:xmpp:jingle:transports:s5b:1"); - if (transport == null) { - transport = this.addChild("transport", - "urn:xmpp:jingle:transports:s5b:1"); - transport.setAttribute("sid", this.transportId); - } - return transport; - } + public Element socks5transport() { + Element transport = this.findChild("transport", Namespace.JINGLE_TRANSPORTS_S5B); + if (transport == null) { + transport = this.addChild("transport", Namespace.JINGLE_TRANSPORTS_S5B); + transport.setAttribute("sid", this.transportId); + } + return transport; + } - public Element ibbTransport() { - Element transport = this.findChild("transport", - "urn:xmpp:jingle:transports:ibb:1"); - if (transport == null) { - transport = this.addChild("transport", - "urn:xmpp:jingle:transports:ibb:1"); - transport.setAttribute("sid", this.transportId); - } - return transport; - } + public Element ibbTransport() { + Element transport = this.findChild("transport", Namespace.JINGLE_TRANSPORTS_IBB); + if (transport == null) { + transport = this.addChild("transport", Namespace.JINGLE_TRANSPORTS_IBB); + transport.setAttribute("sid", this.transportId); + } + return transport; + } - public boolean hasSocks5Transport() { - return this.hasChild("transport", "urn:xmpp:jingle:transports:s5b:1"); - } + public boolean hasSocks5Transport() { + return this.hasChild("transport", Namespace.JINGLE_TRANSPORTS_S5B); + } - public boolean hasIbbTransport() { - return this.hasChild("transport", "urn:xmpp:jingle:transports:ibb:1"); - } + public boolean hasIbbTransport() { + return this.hasChild("transport", Namespace.JINGLE_TRANSPORTS_IBB); + } }