Merge tag '2.5.3' into develop
This commit is contained in:
		
						commit
						b65e14072e
					
				|  | @ -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) | ||||
|  |  | |||
|  | @ -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 | ||||
|  |  | |||
|  | @ -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_", | ||||
|  |  | |||
|  | @ -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, | ||||
|  |  | |||
|  | @ -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) { | ||||
|  |  | |||
|  | @ -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(); | ||||
|  |  | |||
|  | @ -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(); | ||||
|  |  | |||
|  | @ -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()); | ||||
|  |  | |||
|  | @ -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<Message> messages) { | ||||
|     private void modifyForImage(final Builder builder, final Message message, final ArrayList<Message> messages) { | ||||
|         try { | ||||
|             final Bitmap bitmap = mXmppConnectionService.getFileBackend().getThumbnail(message, getPixel(288), false); | ||||
|             final ArrayList<Message> 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<Message> messages) { | ||||
|     private void modifyForTextOnly(final Builder builder,  final ArrayList<Message> 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<CharSequence, Boolean> 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<Message> messages) { | ||||
|  |  | |||
|  | @ -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); | ||||
| 	} | ||||
|  |  | |||
|  | @ -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<Uri> uris = extractUris(extras); | ||||
|         if (uris != null && uris.size() > 0) { | ||||
|             if (uris.size() == 1 && "geo".equals(uris.get(0).getScheme())) { | ||||
|                 mediaPreviewAdapter.addMediaPreviews(Attachment.of(getActivity(), uris.get(0), Attachment.Type.LOCATION)); | ||||
|             } else { | ||||
|                 final List<Uri> 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); | ||||
|  |  | |||
|  | @ -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); | ||||
|  |  | |||
|  | @ -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 { | ||||
|  |  | |||
|  | @ -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) { | ||||
|  |  | |||
|  | @ -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); | ||||
| 						} | ||||
| 					} | ||||
| 				} | ||||
|  |  | |||
|  | @ -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; | ||||
|  |  | |||
|  | @ -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"; | ||||
| } | ||||
|  |  | |||
|  | @ -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(); | ||||
|  |  | |||
|  | @ -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,16 +45,14 @@ import rocks.xmpp.addr.Jid; | |||
| 
 | ||||
| public class JingleConnection implements Transferable { | ||||
| 
 | ||||
|     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 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 Content.Version ftVersion = Content.Version.FT_3; | ||||
| 
 | ||||
|     private int ibbBlockSize = 8192; | ||||
|  | @ -74,6 +73,7 @@ public class JingleConnection implements Transferable { | |||
| 
 | ||||
|     private String contentName; | ||||
|     private String contentCreator; | ||||
|     private Transport initialTransport; | ||||
| 
 | ||||
|     private int mProgress = 0; | ||||
| 
 | ||||
|  | @ -96,16 +96,7 @@ public class JingleConnection implements Transferable { | |||
|         } | ||||
|     }; | ||||
|     private byte[] expectedHash = new byte[0]; | ||||
| 
 | ||||
| 	private boolean responding() { | ||||
| 		return responder != null && responder.equals(account.getJid()); | ||||
| 	} | ||||
| 
 | ||||
| 	private boolean initiating() { | ||||
| 		return initiator.equals(account.getJid()); | ||||
| 	} | ||||
| 
 | ||||
| 	final OnFileTransmissionStatusChanged onFileTransmissionStatusChanged = new OnFileTransmissionStatusChanged() { | ||||
|     private final OnFileTransmissionStatusChanged onFileTransmissionStatusChanged = new OnFileTransmissionStatusChanged() { | ||||
| 
 | ||||
|         @Override | ||||
|         public void onFileTransmitted(DownloadableFile file) { | ||||
|  | @ -113,6 +104,7 @@ public class JingleConnection implements Transferable { | |||
|                 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); | ||||
|  | @ -151,22 +143,19 @@ public class JingleConnection implements Transferable { | |||
|             JingleConnection.this.fail(); | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
| 	InputStream getFileInputStream() { | ||||
| 		return this.mFileInputStream; | ||||
|     private OnTransportConnected onIbbTransportConnected = new OnTransportConnected() { | ||||
|         @Override | ||||
|         public void failed() { | ||||
|             Log.d(Config.LOGTAG, "ibb open failed"); | ||||
|         } | ||||
| 
 | ||||
| 	OutputStream getFileOutputStream() throws IOException { | ||||
| 		if (this.file == null) { | ||||
| 			Log.d(Config.LOGTAG,"file object was not assigned"); | ||||
| 			return null; | ||||
|         @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); | ||||
|         } | ||||
| 		this.file.getParentFile().mkdirs(); | ||||
| 		this.file.createNewFile(); | ||||
| 		this.mFileOutputStream = AbstractConnectionManager.createOutputStream(this.file); | ||||
| 		return this.mFileOutputStream; | ||||
| 	} | ||||
| 
 | ||||
|     }; | ||||
|     private OnProxyActivated onProxyActivated = new OnProxyActivated() { | ||||
| 
 | ||||
|         @Override | ||||
|  | @ -192,6 +181,29 @@ public class JingleConnection implements Transferable { | |||
|                 .getXmppConnectionService(); | ||||
|     } | ||||
| 
 | ||||
|     private boolean responding() { | ||||
|         return responder != null && responder.equals(account.getJid()); | ||||
|     } | ||||
| 
 | ||||
|     private boolean initiating() { | ||||
|         return initiator.equals(account.getJid()); | ||||
|     } | ||||
| 
 | ||||
|     InputStream getFileInputStream() { | ||||
|         return this.mFileInputStream; | ||||
|     } | ||||
| 
 | ||||
|     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; | ||||
|     } | ||||
| 
 | ||||
|     public String getSessionId() { | ||||
|         return this.sessionId; | ||||
|     } | ||||
|  | @ -222,7 +234,6 @@ public class JingleConnection implements Transferable { | |||
|         } 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"); | ||||
|  | @ -263,15 +274,12 @@ public class JingleConnection implements Transferable { | |||
|     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) { | ||||
|             conversation.getAccount().getAxolotlService().prepareKeyTransportMessage(conversation, xmppAxolotlMessage -> { | ||||
|                 if (xmppAxolotlMessage != null) { | ||||
|                     init(message, xmppAxolotlMessage); | ||||
|                 } else { | ||||
|                     fail(); | ||||
|                 } | ||||
| 				} | ||||
|             }); | ||||
|         } else { | ||||
|             init(message, null); | ||||
|  | @ -285,28 +293,23 @@ public class JingleConnection implements Transferable { | |||
|         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.candidates.size() > 0) { | ||||
|         if (this.initialTransport == Transport.IBB) { | ||||
|             this.sendInitRequest(); | ||||
|         } else if (this.candidates.size() > 0) { | ||||
|             this.sendInitRequest(); | ||||
|         } else { | ||||
| 			this.mJingleConnectionManager.getPrimaryCandidate(account, | ||||
| 					new OnPrimaryCandidateFound() { | ||||
| 
 | ||||
| 						@Override | ||||
| 						public void onPrimaryCandidateFound(boolean success, | ||||
| 															final JingleCandidate candidate) { | ||||
|             this.mJingleConnectionManager.getPrimaryCandidate(account, (success, candidate) -> { | ||||
|                 if (success) { | ||||
| 								final JingleSocks5Transport socksConnection = new JingleSocks5Transport( | ||||
| 										JingleConnection.this, candidate); | ||||
| 								connections.put(candidate.getCid(), | ||||
| 										socksConnection); | ||||
| 								socksConnection | ||||
| 										.connect(new OnTransportConnected() { | ||||
|                     final JingleSocks5Transport socksConnection = new JingleSocks5Transport(this, candidate); | ||||
|                     connections.put(candidate.getCid(), socksConnection); | ||||
|                     socksConnection.connect(new OnTransportConnected() { | ||||
| 
 | ||||
|                         @Override | ||||
|                         public void failed() { | ||||
|  | @ -328,26 +331,29 @@ public class JingleConnection implements Transferable { | |||
|                     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<String> features = result.getFeatures(); | ||||
|         List<String> 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; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private List<String> 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(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|  | @ -368,9 +374,32 @@ public class JingleConnection implements Transferable { | |||
|         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); | ||||
| 
 | ||||
|         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(); | ||||
|  | @ -379,8 +408,6 @@ public class JingleConnection implements Transferable { | |||
|         } | ||||
|         this.fileOffer = content.getFileOffer(this.ftVersion); | ||||
| 
 | ||||
| 		mXmppConnectionService.sendIqPacket(account,packet.generateResponse(IqPacket.TYPE.RESULT),null); | ||||
| 
 | ||||
|         if (fileOffer != null) { | ||||
|             Element encrypted = fileOffer.findChild("encrypted", AxolotlService.PEP_PREFIX); | ||||
|             if (encrypted != null) { | ||||
|  | @ -425,6 +452,20 @@ public class JingleConnection implements Transferable { | |||
|                 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()) { | ||||
|  | @ -441,20 +482,6 @@ public class JingleConnection implements Transferable { | |||
|                                     .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(); | ||||
|  | @ -489,13 +516,14 @@ public class JingleConnection implements Transferable { | |||
|                 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,new OnIqPacketReceived() { | ||||
| 
 | ||||
| 				@Override | ||||
| 				public void onIqPacketReceived(Account account, IqPacket packet) { | ||||
| 					if (packet.getType() == IqPacket.TYPE.RESULT) { | ||||
|             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; | ||||
|  | @ -504,8 +532,7 @@ public class JingleConnection implements Transferable { | |||
|                         Log.d(Config.LOGTAG, "received ack for offer when status was " + mJingleStatus); | ||||
|                     } | ||||
|                 } else { | ||||
| 						fail(IqParser.extractErrorMessage(packet)); | ||||
| 					} | ||||
|                     fail(IqParser.extractErrorMessage(response)); | ||||
|                 } | ||||
|             }); | ||||
| 
 | ||||
|  | @ -532,17 +559,21 @@ public class JingleConnection implements Transferable { | |||
|         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) { | ||||
|         if (initialTransport == Transport.SOCKS) { | ||||
|             sendAcceptSocks(); | ||||
|         } else { | ||||
|             sendAcceptIbb(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     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( | ||||
| 							JingleConnection.this, | ||||
| 							candidate); | ||||
|                 final JingleSocks5Transport socksConnection = new JingleSocks5Transport(this, candidate); | ||||
|                 connections.put(candidate.getCid(), socksConnection); | ||||
|                 socksConnection.connect(new OnTransportConnected() { | ||||
| 
 | ||||
|  | @ -572,10 +603,21 @@ public class JingleConnection implements Transferable { | |||
|                 sendJinglePacket(packet); | ||||
|                 connectNextCandidate(); | ||||
|             } | ||||
| 			} | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     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 JinglePacket bootstrapPacket(String action) { | ||||
|         JinglePacket packet = new JinglePacket(); | ||||
|         packet.setAction(action); | ||||
|  | @ -595,13 +637,31 @@ public class JingleConnection implements Transferable { | |||
|     } | ||||
| 
 | ||||
|     private boolean receiveAccept(JinglePacket packet) { | ||||
| 		Content content = packet.getJingleContent(); | ||||
| 		mergeCandidates(JingleCandidate.parse(content.socks5transport() | ||||
| 				.getChildren())); | ||||
|         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; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private boolean receiveTransportInfo(JinglePacket packet) { | ||||
|  | @ -635,8 +695,7 @@ public class JingleConnection implements Transferable { | |||
|                 } | ||||
|                 return true; | ||||
|             } else if (content.socks5transport().hasChild("candidate-used")) { | ||||
| 				String cid = content.socks5transport() | ||||
| 						.findChild("candidate-used").getAttribute("cid"); | ||||
|                 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); | ||||
|  | @ -649,7 +708,7 @@ public class JingleConnection implements Transferable { | |||
|                     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)); | ||||
|                         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 { | ||||
|  | @ -692,19 +751,13 @@ public class JingleConnection implements Transferable { | |||
|                             .setAttribute("sid", sid); | ||||
|                     activation.query().addChild("activate") | ||||
|                             .setContent(this.getCounterPart().toString()); | ||||
| 					mXmppConnectionService.sendIqPacket(account,activation, | ||||
| 							new OnIqPacketReceived() { | ||||
| 
 | ||||
| 								@Override | ||||
| 								public void onIqPacketReceived(Account account, | ||||
| 										IqPacket packet) { | ||||
| 									if (packet.getType() != IqPacket.TYPE.RESULT) { | ||||
|                     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, | ||||
|  | @ -785,17 +838,6 @@ public class JingleConnection implements Transferable { | |||
|         this.sendJinglePacket(packet); | ||||
|     } | ||||
| 
 | ||||
| 	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"); | ||||
|  | @ -811,21 +853,19 @@ public class JingleConnection implements Transferable { | |||
|         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); | ||||
| 
 | ||||
|         final Content content = new Content(contentCreator, contentName); | ||||
|         content.setFileOffer(fileOffer, ftVersion); | ||||
|         content.ibbTransport().setAttribute("block-size", this.ibbBlockSize); | ||||
|         answer.setContent(content); | ||||
| 
 | ||||
| 
 | ||||
|         if (initiating()) { | ||||
| 			this.sendJinglePacket(answer, new OnIqPacketReceived() { | ||||
| 				@Override | ||||
| 				public void onIqPacketReceived(Account account, IqPacket packet) { | ||||
| 					if (packet.getType() == IqPacket.TYPE.RESULT) { | ||||
|             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); | ||||
|  | @ -872,6 +912,7 @@ public class JingleConnection implements Transferable { | |||
|             Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": received session-terminate/success while responding"); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void cancel() { | ||||
|         this.cancelled = true; | ||||
|  | @ -983,8 +1024,7 @@ public class JingleConnection implements Transferable { | |||
|         JinglePacket packet = bootstrapPacket("transport-info"); | ||||
|         Content content = new Content(this.contentCreator, this.contentName); | ||||
|         content.setTransportId(this.transportId); | ||||
| 		content.socks5transport().addChild("activated") | ||||
| 				.setAttribute("cid", cid); | ||||
|         content.socks5transport().addChild("activated").setAttribute("cid", cid); | ||||
|         packet.setContent(content); | ||||
|         this.sendJinglePacket(packet); | ||||
|     } | ||||
|  | @ -1003,7 +1043,7 @@ public class JingleConnection implements Transferable { | |||
|     } | ||||
| 
 | ||||
|     private void sendCandidateError() { | ||||
| 		Log.d(Config.LOGTAG,"sending canditate error"); | ||||
|         Log.d(Config.LOGTAG, "sending candidate error"); | ||||
|         JinglePacket packet = bootstrapPacket("transport-info"); | ||||
|         Content content = new Content(this.contentCreator, this.contentName); | ||||
|         content.setTransportId(this.transportId); | ||||
|  | @ -1053,7 +1093,7 @@ public class JingleConnection implements Transferable { | |||
|         return null; | ||||
|     } | ||||
| 
 | ||||
| 	public void updateProgress(int i) { | ||||
|     void updateProgress(int i) { | ||||
|         this.mProgress = i; | ||||
|         mJingleConnectionManager.updateConversationUi(false); | ||||
|     } | ||||
|  | @ -1066,12 +1106,6 @@ public class JingleConnection implements Transferable { | |||
|         return this.ftVersion; | ||||
|     } | ||||
| 
 | ||||
| 	interface OnProxyActivated { | ||||
| 		void success(); | ||||
| 
 | ||||
| 		void failed(); | ||||
| 	} | ||||
| 
 | ||||
|     public boolean hasTransportId(String sid) { | ||||
|         return sid.equals(this.transportId); | ||||
|     } | ||||
|  | @ -1083,13 +1117,7 @@ public class JingleConnection implements Transferable { | |||
|     public boolean start() { | ||||
|         if (account.getStatus() == Account.State.ONLINE) { | ||||
|             if (mJingleStatus == JINGLE_STATUS_INITIATED) { | ||||
| 				new Thread(new Runnable() { | ||||
| 
 | ||||
| 					@Override | ||||
| 					public void run() { | ||||
| 						sendAccept(); | ||||
| 					} | ||||
| 				}).start(); | ||||
|                 new Thread(this::sendAccept).start(); | ||||
|             } | ||||
|             return true; | ||||
|         } else { | ||||
|  | @ -1119,4 +1147,10 @@ public class JingleConnection implements Transferable { | |||
|     public AbstractConnectionManager getConnectionManager() { | ||||
|         return this.mJingleConnectionManager; | ||||
|     } | ||||
| 
 | ||||
|     interface OnProxyActivated { | ||||
|         void success(); | ||||
| 
 | ||||
|         void failed(); | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -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)); | ||||
|  |  | |||
|  | @ -0,0 +1,5 @@ | |||
| package eu.siacs.conversations.xmpp.jingle; | ||||
| 
 | ||||
| public enum Transport { | ||||
|     SOCKS, IBB | ||||
| } | ||||
|  | @ -2,6 +2,7 @@ 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 { | ||||
| 
 | ||||
|  | @ -102,32 +103,28 @@ public class Content extends Element { | |||
|     } | ||||
| 
 | ||||
|     public Element socks5transport() { | ||||
| 		Element transport = this.findChild("transport", | ||||
| 				"urn:xmpp:jingle:transports:s5b:1"); | ||||
|         Element transport = this.findChild("transport", Namespace.JINGLE_TRANSPORTS_S5B); | ||||
|         if (transport == null) { | ||||
| 			transport = this.addChild("transport", | ||||
| 					"urn:xmpp:jingle:transports:s5b:1"); | ||||
|             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"); | ||||
|         Element transport = this.findChild("transport", Namespace.JINGLE_TRANSPORTS_IBB); | ||||
|         if (transport == null) { | ||||
| 			transport = this.addChild("transport", | ||||
| 					"urn:xmpp:jingle:transports:ibb:1"); | ||||
|             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"); | ||||
|         return this.hasChild("transport", Namespace.JINGLE_TRANSPORTS_S5B); | ||||
|     } | ||||
| 
 | ||||
|     public boolean hasIbbTransport() { | ||||
| 		return this.hasChild("transport", "urn:xmpp:jingle:transports:ibb:1"); | ||||
|         return this.hasChild("transport", Namespace.JINGLE_TRANSPORTS_IBB); | ||||
|     } | ||||
| } | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue