upload files using p1s3 - sending part
This commit is contained in:
		
							parent
							
								
									e4ca8961aa
								
							
						
					
					
						commit
						70d95c7903
					
				| @ -57,7 +57,7 @@ public class Account extends AbstractEntity { | |||||||
| 	public final HashSet<Pair<String, String>> inProgressDiscoFetches = new HashSet<>(); | 	public final HashSet<Pair<String, String>> inProgressDiscoFetches = new HashSet<>(); | ||||||
| 
 | 
 | ||||||
| 	public boolean httpUploadAvailable(long filesize) { | 	public boolean httpUploadAvailable(long filesize) { | ||||||
| 		return xmppConnection != null && xmppConnection.getFeatures().httpUpload(filesize); | 		return xmppConnection != null && (xmppConnection.getFeatures().httpUpload(filesize) || xmppConnection.getFeatures().p1S3FileTransfer()); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	public boolean httpUploadAvailable() { | 	public boolean httpUploadAvailable() { | ||||||
|  | |||||||
| @ -350,6 +350,14 @@ public class IqGenerator extends AbstractGenerator { | |||||||
| 		return packet; | 		return packet; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	public IqPacket requestP1S3Slot(Jid host, String md5) { | ||||||
|  | 		IqPacket packet = new IqPacket(IqPacket.TYPE.SET); | ||||||
|  | 		packet.setTo(host); | ||||||
|  | 		packet.query(Namespace.P1_S3_FILE_TRANSFER).setAttribute("md5",md5); | ||||||
|  | 		Log.d(Config.LOGTAG,packet.toString()); | ||||||
|  | 		return packet; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	private static String convertFilename(String name) { | 	private static String convertFilename(String name) { | ||||||
| 		int pos = name.indexOf('.'); | 		int pos = name.indexOf('.'); | ||||||
| 		if (pos != -1) { | 		if (pos != -1) { | ||||||
|  | |||||||
| @ -1,5 +1,8 @@ | |||||||
| package eu.siacs.conversations.generator; | package eu.siacs.conversations.generator; | ||||||
| 
 | 
 | ||||||
|  | import android.util.Log; | ||||||
|  | 
 | ||||||
|  | import java.net.URL; | ||||||
| import java.text.SimpleDateFormat; | import java.text.SimpleDateFormat; | ||||||
| import java.util.ArrayList; | import java.util.ArrayList; | ||||||
| import java.util.Date; | import java.util.Date; | ||||||
| @ -13,6 +16,7 @@ import eu.siacs.conversations.entities.Account; | |||||||
| import eu.siacs.conversations.entities.Contact; | import eu.siacs.conversations.entities.Contact; | ||||||
| import eu.siacs.conversations.entities.Conversation; | import eu.siacs.conversations.entities.Conversation; | ||||||
| import eu.siacs.conversations.entities.Message; | import eu.siacs.conversations.entities.Message; | ||||||
|  | import eu.siacs.conversations.http.P1S3UrlStreamHandler; | ||||||
| import eu.siacs.conversations.services.XmppConnectionService; | import eu.siacs.conversations.services.XmppConnectionService; | ||||||
| import eu.siacs.conversations.xml.Element; | import eu.siacs.conversations.xml.Element; | ||||||
| import eu.siacs.conversations.xml.Namespace; | import eu.siacs.conversations.xml.Namespace; | ||||||
| @ -42,7 +46,7 @@ public class MessageGenerator extends AbstractGenerator { | |||||||
| 		} else if (message.getType() == Message.TYPE_PRIVATE) { //TODO files and images might be private as well | 		} else if (message.getType() == Message.TYPE_PRIVATE) { //TODO files and images might be private as well | ||||||
| 			packet.setTo(message.getCounterpart()); | 			packet.setTo(message.getCounterpart()); | ||||||
| 			packet.setType(MessagePacket.TYPE_CHAT); | 			packet.setType(MessagePacket.TYPE_CHAT); | ||||||
| 			packet.addChild("x","http://jabber.org/protocol/muc#user"); | 			packet.addChild("x", "http://jabber.org/protocol/muc#user"); | ||||||
| 			if (this.mXmppConnectionService.indicateReceived()) { | 			if (this.mXmppConnectionService.indicateReceived()) { | ||||||
| 				packet.addChild("request", "urn:xmpp:receipts"); | 				packet.addChild("request", "urn:xmpp:receipts"); | ||||||
| 			} | 			} | ||||||
| @ -55,9 +59,9 @@ public class MessageGenerator extends AbstractGenerator { | |||||||
| 		} | 		} | ||||||
| 		packet.setFrom(account.getJid()); | 		packet.setFrom(account.getJid()); | ||||||
| 		packet.setId(message.getUuid()); | 		packet.setId(message.getUuid()); | ||||||
| 		packet.addChild("origin-id", Namespace.STANZA_IDS).setAttribute("id",message.getUuid()); | 		packet.addChild("origin-id", Namespace.STANZA_IDS).setAttribute("id", message.getUuid()); | ||||||
| 		if (message.edited()) { | 		if (message.edited()) { | ||||||
| 			packet.addChild("replace","urn:xmpp:message-correct:0").setAttribute("id",message.getEditedId()); | 			packet.addChild("replace", "urn:xmpp:message-correct:0").setAttribute("id", message.getEditedId()); | ||||||
| 		} | 		} | ||||||
| 		return packet; | 		return packet; | ||||||
| 	} | 	} | ||||||
| @ -79,9 +83,9 @@ public class MessageGenerator extends AbstractGenerator { | |||||||
| 		packet.setAxolotlMessage(axolotlMessage.toElement()); | 		packet.setAxolotlMessage(axolotlMessage.toElement()); | ||||||
| 		packet.setBody(OMEMO_FALLBACK_MESSAGE); | 		packet.setBody(OMEMO_FALLBACK_MESSAGE); | ||||||
| 		packet.addChild("store", "urn:xmpp:hints"); | 		packet.addChild("store", "urn:xmpp:hints"); | ||||||
| 		packet.addChild("encryption","urn:xmpp:eme:0") | 		packet.addChild("encryption", "urn:xmpp:eme:0") | ||||||
| 				.setAttribute("name","OMEMO") | 				.setAttribute("name", "OMEMO") | ||||||
| 				.setAttribute("namespace",AxolotlService.PEP_PREFIX); | 				.setAttribute("namespace", AxolotlService.PEP_PREFIX); | ||||||
| 		return packet; | 		return packet; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| @ -99,8 +103,16 @@ public class MessageGenerator extends AbstractGenerator { | |||||||
| 		String content; | 		String content; | ||||||
| 		if (message.hasFileOnRemoteHost()) { | 		if (message.hasFileOnRemoteHost()) { | ||||||
| 			Message.FileParams fileParams = message.getFileParams(); | 			Message.FileParams fileParams = message.getFileParams(); | ||||||
| 			content = fileParams.url.toString(); | 			final URL url = fileParams.url; | ||||||
| 			packet.addChild("x",Namespace.OOB).addChild("url").setContent(content); | 			if (P1S3UrlStreamHandler.PROTOCOL_NAME.equals(url.getProtocol())) { | ||||||
|  | 				Element x = packet.addChild("x", Namespace.P1_S3_FILE_TRANSFER); | ||||||
|  | 				x.setAttribute("name", url.getFile()); | ||||||
|  | 				x.setAttribute("fileid", url.getHost()); | ||||||
|  | 				return packet; | ||||||
|  | 			} else { | ||||||
|  | 				content = url.toString(); | ||||||
|  | 				packet.addChild("x", Namespace.OOB).addChild("url").setContent(content); | ||||||
|  | 			} | ||||||
| 		} else { | 		} else { | ||||||
| 			content = message.getBody(); | 			content = message.getBody(); | ||||||
| 		} | 		} | ||||||
| @ -113,7 +125,7 @@ public class MessageGenerator extends AbstractGenerator { | |||||||
| 		if (message.hasFileOnRemoteHost()) { | 		if (message.hasFileOnRemoteHost()) { | ||||||
| 			final String url = message.getFileParams().url.toString(); | 			final String url = message.getFileParams().url.toString(); | ||||||
| 			packet.setBody(url); | 			packet.setBody(url); | ||||||
| 			packet.addChild("x",Namespace.OOB).addChild("url").setContent(url); | 			packet.addChild("x", Namespace.OOB).addChild("url").setContent(url); | ||||||
| 		} else { | 		} else { | ||||||
| 			if (Config.supportUnencrypted()) { | 			if (Config.supportUnencrypted()) { | ||||||
| 				packet.setBody(PGP_FALLBACK_MESSAGE); | 				packet.setBody(PGP_FALLBACK_MESSAGE); | ||||||
| @ -146,16 +158,16 @@ public class MessageGenerator extends AbstractGenerator { | |||||||
| 		packet.setType(groupChat ? MessagePacket.TYPE_GROUPCHAT : MessagePacket.TYPE_CHAT); | 		packet.setType(groupChat ? MessagePacket.TYPE_GROUPCHAT : MessagePacket.TYPE_CHAT); | ||||||
| 		packet.setTo(groupChat ? to.asBareJid() : to); | 		packet.setTo(groupChat ? to.asBareJid() : to); | ||||||
| 		packet.setFrom(account.getJid()); | 		packet.setFrom(account.getJid()); | ||||||
| 		Element displayed = packet.addChild("displayed","urn:xmpp:chat-markers:0"); | 		Element displayed = packet.addChild("displayed", "urn:xmpp:chat-markers:0"); | ||||||
| 		displayed.setAttribute("id", id); | 		displayed.setAttribute("id", id); | ||||||
| 		if (groupChat && counterpart != null) { | 		if (groupChat && counterpart != null) { | ||||||
| 			displayed.setAttribute("sender",counterpart.toString()); | 			displayed.setAttribute("sender", counterpart.toString()); | ||||||
| 		} | 		} | ||||||
| 		packet.addChild("store", "urn:xmpp:hints"); | 		packet.addChild("store", "urn:xmpp:hints"); | ||||||
| 		return packet; | 		return packet; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	public MessagePacket conferenceSubject(Conversation conversation,String subject) { | 	public MessagePacket conferenceSubject(Conversation conversation, String subject) { | ||||||
| 		MessagePacket packet = new MessagePacket(); | 		MessagePacket packet = new MessagePacket(); | ||||||
| 		packet.setType(MessagePacket.TYPE_GROUPCHAT); | 		packet.setType(MessagePacket.TYPE_GROUPCHAT); | ||||||
| 		packet.setTo(conversation.getJid().asBareJid()); | 		packet.setTo(conversation.getJid().asBareJid()); | ||||||
| @ -175,7 +187,7 @@ public class MessageGenerator extends AbstractGenerator { | |||||||
| 		x.setAttribute("jid", conversation.getJid().asBareJid().toString()); | 		x.setAttribute("jid", conversation.getJid().asBareJid().toString()); | ||||||
| 		String password = conversation.getMucOptions().getPassword(); | 		String password = conversation.getMucOptions().getPassword(); | ||||||
| 		if (password != null) { | 		if (password != null) { | ||||||
| 			x.setAttribute("password",password); | 			x.setAttribute("password", password); | ||||||
| 		} | 		} | ||||||
| 		return packet; | 		return packet; | ||||||
| 	} | 	} | ||||||
| @ -198,7 +210,7 @@ public class MessageGenerator extends AbstractGenerator { | |||||||
| 		receivedPacket.setType(type); | 		receivedPacket.setType(type); | ||||||
| 		receivedPacket.setTo(originalMessage.getFrom()); | 		receivedPacket.setTo(originalMessage.getFrom()); | ||||||
| 		receivedPacket.setFrom(account.getJid()); | 		receivedPacket.setFrom(account.getJid()); | ||||||
| 		for(String namespace : namespaces) { | 		for (String namespace : namespaces) { | ||||||
| 			receivedPacket.addChild("received", namespace).setAttribute("id", originalMessage.getId()); | 			receivedPacket.addChild("received", namespace).setAttribute("id", originalMessage.getId()); | ||||||
| 		} | 		} | ||||||
| 		receivedPacket.addChild("store", "urn:xmpp:hints"); | 		receivedPacket.addChild("store", "urn:xmpp:hints"); | ||||||
| @ -209,7 +221,7 @@ public class MessageGenerator extends AbstractGenerator { | |||||||
| 		MessagePacket packet = new MessagePacket(); | 		MessagePacket packet = new MessagePacket(); | ||||||
| 		packet.setFrom(account.getJid()); | 		packet.setFrom(account.getJid()); | ||||||
| 		packet.setTo(to); | 		packet.setTo(to); | ||||||
| 		packet.addChild("received","urn:xmpp:receipts").setAttribute("id",id); | 		packet.addChild("received", "urn:xmpp:receipts").setAttribute("id", id); | ||||||
| 		packet.addChild("store", "urn:xmpp:hints"); | 		packet.addChild("store", "urn:xmpp:hints"); | ||||||
| 		return packet; | 		return packet; | ||||||
| 	} | 	} | ||||||
|  | |||||||
| @ -3,11 +3,14 @@ package eu.siacs.conversations.http; | |||||||
| import java.net.URLStreamHandler; | import java.net.URLStreamHandler; | ||||||
| import java.net.URLStreamHandlerFactory; | import java.net.URLStreamHandlerFactory; | ||||||
| 
 | 
 | ||||||
| public class AesGcmURLStreamHandlerFactory implements URLStreamHandlerFactory { | public class CustomURLStreamHandlerFactory implements URLStreamHandlerFactory { | ||||||
|  | 
 | ||||||
|     @Override |     @Override | ||||||
|     public URLStreamHandler createURLStreamHandler(String protocol) { |     public URLStreamHandler createURLStreamHandler(String protocol) { | ||||||
|         if (AesGcmURLStreamHandler.PROTOCOL_NAME.equals(protocol)) { |         if (AesGcmURLStreamHandler.PROTOCOL_NAME.equals(protocol)) { | ||||||
|             return new AesGcmURLStreamHandler(); |             return new AesGcmURLStreamHandler(); | ||||||
|  |         } else if (P1S3UrlStreamHandler.PROTOCOL_NAME.equals(protocol)) { | ||||||
|  |             return new P1S3UrlStreamHandler(); | ||||||
|         } else { |         } else { | ||||||
|             return null; |             return null; | ||||||
|         } |         } | ||||||
| @ -46,11 +46,10 @@ public class HttpConnectionManager extends AbstractConnectionManager { | |||||||
| 		return connection; | 		return connection; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	public HttpUploadConnection createNewUploadConnection(Message message, boolean delay) { | 	public void createNewUploadConnection(Message message, boolean delay) { | ||||||
| 		HttpUploadConnection connection = new HttpUploadConnection(this); | 		HttpUploadConnection connection = new HttpUploadConnection(Method.determine(message.getConversation().getAccount()), this); | ||||||
| 		connection.init(message,delay); | 		connection.init(message,delay); | ||||||
| 		this.uploadConnections.add(connection); | 		this.uploadConnections.add(connection); | ||||||
| 		return connection; |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	public void finishConnection(HttpDownloadConnection connection) { | 	public void finishConnection(HttpDownloadConnection connection) { | ||||||
|  | |||||||
| @ -1,9 +1,12 @@ | |||||||
| package eu.siacs.conversations.http; | package eu.siacs.conversations.http; | ||||||
| 
 | 
 | ||||||
| import android.os.PowerManager; | import android.os.PowerManager; | ||||||
|  | import android.renderscript.ScriptGroup; | ||||||
| import android.util.Log; | import android.util.Log; | ||||||
| import android.util.Pair; | import android.util.Pair; | ||||||
| 
 | 
 | ||||||
|  | import org.bouncycastle.jce.exception.ExtIOException; | ||||||
|  | 
 | ||||||
| import java.io.FileNotFoundException; | import java.io.FileNotFoundException; | ||||||
| import java.io.IOException; | import java.io.IOException; | ||||||
| import java.io.InputStream; | import java.io.InputStream; | ||||||
| @ -14,6 +17,7 @@ import java.net.URL; | |||||||
| import java.util.Arrays; | import java.util.Arrays; | ||||||
| import java.util.HashMap; | import java.util.HashMap; | ||||||
| import java.util.List; | import java.util.List; | ||||||
|  | import java.util.Scanner; | ||||||
| 
 | 
 | ||||||
| import javax.net.ssl.HttpsURLConnection; | import javax.net.ssl.HttpsURLConnection; | ||||||
| 
 | 
 | ||||||
| @ -26,6 +30,7 @@ import eu.siacs.conversations.parser.IqParser; | |||||||
| import eu.siacs.conversations.persistance.FileBackend; | import eu.siacs.conversations.persistance.FileBackend; | ||||||
| import eu.siacs.conversations.services.AbstractConnectionManager; | import eu.siacs.conversations.services.AbstractConnectionManager; | ||||||
| import eu.siacs.conversations.services.XmppConnectionService; | import eu.siacs.conversations.services.XmppConnectionService; | ||||||
|  | import eu.siacs.conversations.utils.Checksum; | ||||||
| import eu.siacs.conversations.utils.CryptoHelper; | import eu.siacs.conversations.utils.CryptoHelper; | ||||||
| import eu.siacs.conversations.utils.WakeLockHelper; | import eu.siacs.conversations.utils.WakeLockHelper; | ||||||
| import eu.siacs.conversations.xml.Namespace; | import eu.siacs.conversations.xml.Namespace; | ||||||
| @ -35,24 +40,24 @@ import rocks.xmpp.addr.Jid; | |||||||
| 
 | 
 | ||||||
| public class HttpUploadConnection implements Transferable { | public class HttpUploadConnection implements Transferable { | ||||||
| 
 | 
 | ||||||
| 	private static final List<String> WHITE_LISTED_HEADERS = Arrays.asList( | 	public static final List<String> WHITE_LISTED_HEADERS = Arrays.asList( | ||||||
| 			"Authorization", | 			"Authorization", | ||||||
| 			"Cookie", | 			"Cookie", | ||||||
| 			"Expires" | 			"Expires" | ||||||
| 	); | 	); | ||||||
| 
 | 
 | ||||||
| 	private HttpConnectionManager mHttpConnectionManager; | 	private final HttpConnectionManager mHttpConnectionManager; | ||||||
| 	private XmppConnectionService mXmppConnectionService; | 	private final XmppConnectionService mXmppConnectionService; | ||||||
|  | 	private final SlotRequester mSlotRequester; | ||||||
|  | 	private final Method method; | ||||||
| 
 | 
 | ||||||
| 	private boolean canceled = false; | 	private boolean canceled = false; | ||||||
| 	private boolean delayed = false; | 	private boolean delayed = false; | ||||||
| 	private DownloadableFile file; | 	private DownloadableFile file; | ||||||
| 	private Message message; | 	private Message message; | ||||||
| 	private String mime; | 	private String mime; | ||||||
| 	private URL mGetUrl; | 	private SlotRequester.Slot slot; | ||||||
| 	private URL mPutUrl; | 	private final boolean mUseTor; | ||||||
| 	private HashMap<String,String> mPutHeaders; |  | ||||||
| 	private boolean mUseTor = false; |  | ||||||
| 
 | 
 | ||||||
| 	private byte[] key = null; | 	private byte[] key = null; | ||||||
| 
 | 
 | ||||||
| @ -60,9 +65,11 @@ public class HttpUploadConnection implements Transferable { | |||||||
| 
 | 
 | ||||||
| 	private InputStream mFileInputStream; | 	private InputStream mFileInputStream; | ||||||
| 
 | 
 | ||||||
| 	public HttpUploadConnection(HttpConnectionManager httpConnectionManager) { | 	public HttpUploadConnection(Method method, HttpConnectionManager httpConnectionManager) { | ||||||
|  | 		this.method = method; | ||||||
| 		this.mHttpConnectionManager = httpConnectionManager; | 		this.mHttpConnectionManager = httpConnectionManager; | ||||||
| 		this.mXmppConnectionService = httpConnectionManager.getXmppConnectionService(); | 		this.mXmppConnectionService = httpConnectionManager.getXmppConnectionService(); | ||||||
|  | 		this.mSlotRequester = new SlotRequester(this.mXmppConnectionService); | ||||||
| 		this.mUseTor = mXmppConnectionService.useTorToConnect(); | 		this.mUseTor = mXmppConnectionService.useTorToConnect(); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| @ -118,6 +125,21 @@ public class HttpUploadConnection implements Transferable { | |||||||
| 			mXmppConnectionService.getRNG().nextBytes(this.key); | 			mXmppConnectionService.getRNG().nextBytes(this.key); | ||||||
| 			this.file.setKeyAndIv(this.key); | 			this.file.setKeyAndIv(this.key); | ||||||
| 		} | 		} | ||||||
|  | 
 | ||||||
|  | 		final String md5; | ||||||
|  | 
 | ||||||
|  | 		if (method == Method.P1_S3) { | ||||||
|  | 			try { | ||||||
|  | 				md5 = Checksum.md5(AbstractConnectionManager.createInputStream(file, true).first); | ||||||
|  | 			} catch (Exception e) { | ||||||
|  | 				Log.d(Config.LOGTAG, account.getJid().asBareJid()+": unable to calculate md5()", e); | ||||||
|  | 				fail(e.getMessage()); | ||||||
|  | 				return; | ||||||
|  | 			} | ||||||
|  | 		} else { | ||||||
|  | 			md5 = null; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
| 		Pair<InputStream,Integer> pair; | 		Pair<InputStream,Integer> pair; | ||||||
| 		try { | 		try { | ||||||
| 			pair = AbstractConnectionManager.createInputStream(file, true); | 			pair = AbstractConnectionManager.createInputStream(file, true); | ||||||
| @ -129,42 +151,20 @@ public class HttpUploadConnection implements Transferable { | |||||||
| 		this.file.setExpectedSize(pair.second); | 		this.file.setExpectedSize(pair.second); | ||||||
| 		message.resetFileParams(); | 		message.resetFileParams(); | ||||||
| 		this.mFileInputStream = pair.first; | 		this.mFileInputStream = pair.first; | ||||||
| 		Jid host = account.getXmppConnection().findDiscoItemByFeature(Namespace.HTTP_UPLOAD); | 		this.mSlotRequester.request(method, account, file, mime, md5, new SlotRequester.OnSlotRequested() { | ||||||
| 		IqPacket request = mXmppConnectionService.getIqGenerator().requestHttpUploadSlot(host,file,mime); | 			@Override | ||||||
| 		mXmppConnectionService.sendIqPacket(account, request, (a, packet) -> { | 			public void success(SlotRequester.Slot slot) { | ||||||
| 			if (packet.getType() == IqPacket.TYPE.RESULT) { |  | ||||||
| 				Element slot = packet.findChild("slot", Namespace.HTTP_UPLOAD); |  | ||||||
| 				if (slot != null) { |  | ||||||
| 					try { |  | ||||||
| 						final Element put = slot.findChild("put"); |  | ||||||
| 						final Element get = slot.findChild("get"); |  | ||||||
| 						final String putUrl = put == null ? null : put.getAttribute("url"); |  | ||||||
| 						final String getUrl = get == null ? null : get.getAttribute("url"); |  | ||||||
| 						if (getUrl != null && putUrl != null) { |  | ||||||
| 							this.mGetUrl = new URL(getUrl); |  | ||||||
| 							this.mPutUrl = new URL(putUrl); |  | ||||||
| 							this.mPutHeaders = new HashMap<>(); |  | ||||||
| 							for(Element child : put.getChildren()) { |  | ||||||
| 								if ("header".equals(child.getName())) { |  | ||||||
| 									final String name = child.getAttribute("name"); |  | ||||||
| 									final String value = child.getContent(); |  | ||||||
| 									if (WHITE_LISTED_HEADERS.contains(name) && value != null && !value.trim().contains("\n")) { |  | ||||||
| 										this.mPutHeaders.put(name,value.trim()); |  | ||||||
| 									} |  | ||||||
| 								} |  | ||||||
| 							} |  | ||||||
| 				if (!canceled) { | 				if (!canceled) { | ||||||
| 								new Thread(this::upload).start(); | 					HttpUploadConnection.this.slot = slot; | ||||||
| 							} | 					Log.d(Config.LOGTAG,"not starting upload to "+slot.getPutUrl()); | ||||||
| 							return; | 					new Thread(HttpUploadConnection.this::upload).start(); | ||||||
| 						} |  | ||||||
| 					} catch (MalformedURLException e) { |  | ||||||
| 						//fall through |  | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
|  | 
 | ||||||
|  | 			@Override | ||||||
|  | 			public void failure(String message) { | ||||||
|  | 				fail(message); | ||||||
| 			} | 			} | ||||||
| 			Log.d(Config.LOGTAG,account.getJid().toString()+": invalid response to slot request "+packet); |  | ||||||
| 			fail(IqParser.extractErrorMessage(packet)); |  | ||||||
| 		}); | 		}); | ||||||
| 		message.setTransferable(this); | 		message.setTransferable(this); | ||||||
| 		mXmppConnectionService.markMessage(message, Message.STATUS_UNSEND); | 		mXmppConnectionService.markMessage(message, Message.STATUS_UNSEND); | ||||||
| @ -178,11 +178,11 @@ public class HttpUploadConnection implements Transferable { | |||||||
| 			final int expectedFileSize = (int) file.getExpectedSize(); | 			final int expectedFileSize = (int) file.getExpectedSize(); | ||||||
| 			final int readTimeout = (expectedFileSize / 2048) + Config.SOCKET_TIMEOUT; //assuming a minimum transfer speed of 16kbit/s | 			final int readTimeout = (expectedFileSize / 2048) + Config.SOCKET_TIMEOUT; //assuming a minimum transfer speed of 16kbit/s | ||||||
| 			wakeLock.acquire(readTimeout); | 			wakeLock.acquire(readTimeout); | ||||||
| 			Log.d(Config.LOGTAG, "uploading to " + mPutUrl.toString()+ " w/ read timeout of "+readTimeout+"s"); | 			Log.d(Config.LOGTAG, "uploading to " + slot.getPutUrl().toString()+ " w/ read timeout of "+readTimeout+"s"); | ||||||
| 			if (mUseTor) { | 			if (mUseTor) { | ||||||
| 				connection = (HttpURLConnection) mPutUrl.openConnection(HttpConnectionManager.getProxy()); | 				connection = (HttpURLConnection) slot.getPutUrl().openConnection(HttpConnectionManager.getProxy()); | ||||||
| 			} else { | 			} else { | ||||||
| 				connection = (HttpURLConnection) mPutUrl.openConnection(); | 				connection = (HttpURLConnection) slot.getPutUrl().openConnection(); | ||||||
| 			} | 			} | ||||||
| 			if (connection instanceof HttpsURLConnection) { | 			if (connection instanceof HttpsURLConnection) { | ||||||
| 				mHttpConnectionManager.setupTrustManager((HttpsURLConnection) connection, true); | 				mHttpConnectionManager.setupTrustManager((HttpsURLConnection) connection, true); | ||||||
| @ -190,14 +190,14 @@ public class HttpUploadConnection implements Transferable { | |||||||
| 			connection.setUseCaches(false); | 			connection.setUseCaches(false); | ||||||
| 			connection.setRequestMethod("PUT"); | 			connection.setRequestMethod("PUT"); | ||||||
| 			connection.setFixedLengthStreamingMode(expectedFileSize); | 			connection.setFixedLengthStreamingMode(expectedFileSize); | ||||||
| 			connection.setRequestProperty("Content-Type", mime == null ? "application/octet-stream" : mime); |  | ||||||
| 			connection.setRequestProperty("User-Agent",mXmppConnectionService.getIqGenerator().getIdentityName()); | 			connection.setRequestProperty("User-Agent",mXmppConnectionService.getIqGenerator().getIdentityName()); | ||||||
| 			if(mPutHeaders != null) { | 			if(slot.getHeaders() != null) { | ||||||
| 				for(HashMap.Entry<String,String> entry : mPutHeaders.entrySet()) { | 				for(HashMap.Entry<String,String> entry : slot.getHeaders().entrySet()) { | ||||||
| 					connection.setRequestProperty(entry.getKey(),entry.getValue()); | 					connection.setRequestProperty(entry.getKey(),entry.getValue()); | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| 			connection.setDoOutput(true); | 			connection.setDoOutput(true); | ||||||
|  | 			connection.setDoInput(true); | ||||||
| 			connection.setConnectTimeout(Config.SOCKET_TIMEOUT * 1000); | 			connection.setConnectTimeout(Config.SOCKET_TIMEOUT * 1000); | ||||||
| 			connection.setReadTimeout(readTimeout * 1000); | 			connection.setReadTimeout(readTimeout * 1000); | ||||||
| 			connection.connect(); | 			connection.connect(); | ||||||
| @ -214,12 +214,22 @@ public class HttpUploadConnection implements Transferable { | |||||||
| 			os.close(); | 			os.close(); | ||||||
| 			mFileInputStream.close(); | 			mFileInputStream.close(); | ||||||
| 			int code = connection.getResponseCode(); | 			int code = connection.getResponseCode(); | ||||||
|  | 			InputStream is = connection.getErrorStream(); | ||||||
|  | 			if (is != null) { | ||||||
|  | 				try (Scanner scanner = new Scanner(is)) { | ||||||
|  | 					scanner.useDelimiter("\\Z"); | ||||||
|  | 					Log.d(Config.LOGTAG, "body: " + scanner.next()); | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
| 			if (code == 200 || code == 201) { | 			if (code == 200 || code == 201) { | ||||||
| 				Log.d(Config.LOGTAG, "finished uploading file"); | 				Log.d(Config.LOGTAG, "finished uploading file"); | ||||||
|  | 				final URL get; | ||||||
| 				if (key != null) { | 				if (key != null) { | ||||||
| 					mGetUrl = CryptoHelper.toAesGcmUrl(new URL(mGetUrl.toString() + "#" + CryptoHelper.bytesToHex(key))); | 					get = CryptoHelper.toAesGcmUrl(new URL(slot.getGetUrl().toString() + "#" + CryptoHelper.bytesToHex(key))); | ||||||
|  | 				} else { | ||||||
|  | 					get = slot.getGetUrl(); | ||||||
| 				} | 				} | ||||||
| 				mXmppConnectionService.getFileBackend().updateFileParams(message, mGetUrl); | 				mXmppConnectionService.getFileBackend().updateFileParams(message, get); | ||||||
| 				mXmppConnectionService.getFileBackend().updateMediaScanner(file); | 				mXmppConnectionService.getFileBackend().updateMediaScanner(file); | ||||||
| 				message.setTransferable(null); | 				message.setTransferable(null); | ||||||
| 				message.setCounterpart(message.getConversation().getJid().asBareJid()); | 				message.setCounterpart(message.getConversation().getJid().asBareJid()); | ||||||
|  | |||||||
							
								
								
									
										51
									
								
								src/main/java/eu/siacs/conversations/http/Method.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								src/main/java/eu/siacs/conversations/http/Method.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,51 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright (c) 2018, Daniel Gultsch All rights reserved. | ||||||
|  |  * | ||||||
|  |  * Redistribution and use in source and binary forms, with or without modification, | ||||||
|  |  * are permitted provided that the following conditions are met: | ||||||
|  |  * | ||||||
|  |  * 1. Redistributions of source code must retain the above copyright notice, this | ||||||
|  |  * list of conditions and the following disclaimer. | ||||||
|  |  * | ||||||
|  |  * 2. Redistributions in binary form must reproduce the above copyright notice, | ||||||
|  |  * this list of conditions and the following disclaimer in the documentation and/or | ||||||
|  |  * other materials provided with the distribution. | ||||||
|  |  * | ||||||
|  |  * 3. Neither the name of the copyright holder nor the names of its contributors | ||||||
|  |  * may be used to endorse or promote products derived from this software without | ||||||
|  |  * specific prior written permission. | ||||||
|  |  * | ||||||
|  |  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND | ||||||
|  |  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||||||
|  |  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||||||
|  |  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR | ||||||
|  |  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||||||
|  |  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||||||
|  |  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON | ||||||
|  |  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||||
|  |  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||||||
|  |  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package eu.siacs.conversations.http; | ||||||
|  | 
 | ||||||
|  | import eu.siacs.conversations.entities.Account; | ||||||
|  | import eu.siacs.conversations.xmpp.XmppConnection; | ||||||
|  | 
 | ||||||
|  | public enum  Method { | ||||||
|  | 	P1_S3, HTTP_UPLOAD; | ||||||
|  | 
 | ||||||
|  | 	public static Method determine(Account account) { | ||||||
|  | 		XmppConnection.Features features = account.getXmppConnection() == null ? null : account.getXmppConnection().getFeatures(); | ||||||
|  | 		if (features == null) { | ||||||
|  | 			return HTTP_UPLOAD; | ||||||
|  | 		} | ||||||
|  | 		if (features.httpUpload(0)) { | ||||||
|  | 			return HTTP_UPLOAD; | ||||||
|  | 		} else if (features.p1S3FileTransfer()) { | ||||||
|  | 			return P1_S3; | ||||||
|  | 		} else { | ||||||
|  | 			return HTTP_UPLOAD; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @ -0,0 +1,50 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright (c) 2018, Daniel Gultsch All rights reserved. | ||||||
|  |  * | ||||||
|  |  * Redistribution and use in source and binary forms, with or without modification, | ||||||
|  |  * are permitted provided that the following conditions are met: | ||||||
|  |  * | ||||||
|  |  * 1. Redistributions of source code must retain the above copyright notice, this | ||||||
|  |  * list of conditions and the following disclaimer. | ||||||
|  |  * | ||||||
|  |  * 2. Redistributions in binary form must reproduce the above copyright notice, | ||||||
|  |  * this list of conditions and the following disclaimer in the documentation and/or | ||||||
|  |  * other materials provided with the distribution. | ||||||
|  |  * | ||||||
|  |  * 3. Neither the name of the copyright holder nor the names of its contributors | ||||||
|  |  * may be used to endorse or promote products derived from this software without | ||||||
|  |  * specific prior written permission. | ||||||
|  |  * | ||||||
|  |  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND | ||||||
|  |  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||||||
|  |  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||||||
|  |  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR | ||||||
|  |  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||||||
|  |  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||||||
|  |  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON | ||||||
|  |  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||||
|  |  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||||||
|  |  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package eu.siacs.conversations.http; | ||||||
|  | 
 | ||||||
|  | import java.io.IOException; | ||||||
|  | import java.net.MalformedURLException; | ||||||
|  | import java.net.URL; | ||||||
|  | import java.net.URLConnection; | ||||||
|  | import java.net.URLStreamHandler; | ||||||
|  | 
 | ||||||
|  | public class P1S3UrlStreamHandler extends URLStreamHandler { | ||||||
|  | 
 | ||||||
|  | 	public static final String PROTOCOL_NAME = "p1s3"; | ||||||
|  | 
 | ||||||
|  | 	@Override | ||||||
|  | 	protected URLConnection openConnection(URL url) { | ||||||
|  | 		throw new IllegalStateException("Unable to open connection with stub protocol"); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	public static URL of(String fileId, String filename) throws MalformedURLException { | ||||||
|  | 		return new URL(PROTOCOL_NAME+"://" + fileId + "/" + filename); | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										160
									
								
								src/main/java/eu/siacs/conversations/http/SlotRequester.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										160
									
								
								src/main/java/eu/siacs/conversations/http/SlotRequester.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,160 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright (c) 2018, Daniel Gultsch All rights reserved. | ||||||
|  |  * | ||||||
|  |  * Redistribution and use in source and binary forms, with or without modification, | ||||||
|  |  * are permitted provided that the following conditions are met: | ||||||
|  |  * | ||||||
|  |  * 1. Redistributions of source code must retain the above copyright notice, this | ||||||
|  |  * list of conditions and the following disclaimer. | ||||||
|  |  * | ||||||
|  |  * 2. Redistributions in binary form must reproduce the above copyright notice, | ||||||
|  |  * this list of conditions and the following disclaimer in the documentation and/or | ||||||
|  |  * other materials provided with the distribution. | ||||||
|  |  * | ||||||
|  |  * 3. Neither the name of the copyright holder nor the names of its contributors | ||||||
|  |  * may be used to endorse or promote products derived from this software without | ||||||
|  |  * specific prior written permission. | ||||||
|  |  * | ||||||
|  |  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND | ||||||
|  |  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||||||
|  |  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||||||
|  |  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR | ||||||
|  |  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||||||
|  |  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||||||
|  |  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON | ||||||
|  |  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||||
|  |  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||||||
|  |  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package eu.siacs.conversations.http; | ||||||
|  | 
 | ||||||
|  | import android.util.Log; | ||||||
|  | 
 | ||||||
|  | import java.io.File; | ||||||
|  | import java.net.MalformedURLException; | ||||||
|  | import java.net.URL; | ||||||
|  | import java.util.HashMap; | ||||||
|  | 
 | ||||||
|  | import eu.siacs.conversations.Config; | ||||||
|  | import eu.siacs.conversations.entities.Account; | ||||||
|  | import eu.siacs.conversations.entities.DownloadableFile; | ||||||
|  | import eu.siacs.conversations.parser.IqParser; | ||||||
|  | import eu.siacs.conversations.services.XmppConnectionService; | ||||||
|  | import eu.siacs.conversations.xml.Element; | ||||||
|  | import eu.siacs.conversations.xml.Namespace; | ||||||
|  | import eu.siacs.conversations.xmpp.stanzas.IqPacket; | ||||||
|  | import rocks.xmpp.addr.Jid; | ||||||
|  | 
 | ||||||
|  | public class SlotRequester { | ||||||
|  | 
 | ||||||
|  | 	private XmppConnectionService service; | ||||||
|  | 
 | ||||||
|  | 	public SlotRequester(XmppConnectionService service) { | ||||||
|  | 		this.service = service; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	public void request(Method method, Account account, DownloadableFile file, String mime, String md5, OnSlotRequested callback) { | ||||||
|  | 		if (method == Method.HTTP_UPLOAD) { | ||||||
|  | 			Jid host = account.getXmppConnection().findDiscoItemByFeature(Namespace.HTTP_UPLOAD); | ||||||
|  | 			requestHttpUpload(account, host, file, mime, callback); | ||||||
|  | 		} else { | ||||||
|  | 			requestP1S3(account, Jid.of(account.getServer()), file.getName(), md5, callback); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	private void requestHttpUpload(Account account, Jid host, DownloadableFile file, String mime, OnSlotRequested callback) { | ||||||
|  | 		IqPacket request = service.getIqGenerator().requestHttpUploadSlot(host, file, mime); | ||||||
|  | 		service.sendIqPacket(account, request, (a, packet) -> { | ||||||
|  | 			if (packet.getType() == IqPacket.TYPE.RESULT) { | ||||||
|  | 				Element slotElement = packet.findChild("slot", Namespace.HTTP_UPLOAD); | ||||||
|  | 				if (slotElement != null) { | ||||||
|  | 					try { | ||||||
|  | 						final Element put = slotElement.findChild("put"); | ||||||
|  | 						final Element get = slotElement.findChild("get"); | ||||||
|  | 						final String putUrl = put == null ? null : put.getAttribute("url"); | ||||||
|  | 						final String getUrl = get == null ? null : get.getAttribute("url"); | ||||||
|  | 						if (getUrl != null && putUrl != null) { | ||||||
|  | 							Slot slot = new Slot(new URL(putUrl)); | ||||||
|  | 							slot.getUrl = new URL(getUrl); | ||||||
|  | 							slot.headers = new HashMap<>(); | ||||||
|  | 							for (Element child : put.getChildren()) { | ||||||
|  | 								if ("header".equals(child.getName())) { | ||||||
|  | 									final String name = child.getAttribute("name"); | ||||||
|  | 									final String value = child.getContent(); | ||||||
|  | 									if (HttpUploadConnection.WHITE_LISTED_HEADERS.contains(name) && value != null && !value.trim().contains("\n")) { | ||||||
|  | 										slot.headers.put(name, value.trim()); | ||||||
|  | 									} | ||||||
|  | 									slot.headers.put("Content-Type", mime == null ? "application/octet-stream" : mime); | ||||||
|  | 								} | ||||||
|  | 							} | ||||||
|  | 							callback.success(slot); | ||||||
|  | 							return; | ||||||
|  | 						} | ||||||
|  | 					} catch (MalformedURLException e) { | ||||||
|  | 						//fall through | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 			Log.d(Config.LOGTAG, account.getJid().toString() + ": invalid response to slot request " + packet); | ||||||
|  | 			callback.failure(IqParser.extractErrorMessage(packet)); | ||||||
|  | 		}); | ||||||
|  | 
 | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	private void requestP1S3(final Account account, Jid host, String filename, String md5, OnSlotRequested callback) { | ||||||
|  | 		IqPacket request = service.getIqGenerator().requestP1S3Slot(host, md5); | ||||||
|  | 		service.sendIqPacket(account, request, (a, packet) -> { | ||||||
|  | 			if (packet.getType() == IqPacket.TYPE.RESULT) { | ||||||
|  | 				String putUrl = packet.query(Namespace.P1_S3_FILE_TRANSFER).getAttribute("upload"); | ||||||
|  | 				String id = packet.query().getAttribute("fileid"); | ||||||
|  | 				try { | ||||||
|  | 					if (putUrl != null && id != null) { | ||||||
|  | 						Slot slot = new Slot(new URL(putUrl)); | ||||||
|  | 						slot.getUrl = P1S3UrlStreamHandler.of(id, filename); | ||||||
|  | 						slot.headers = new HashMap<>(); | ||||||
|  | 						slot.headers.put("Content-MD5", md5); | ||||||
|  | 						slot.headers.put("Content-Type", " "); //required to force it to empty. otherwise library will set something | ||||||
|  | 						callback.success(slot); | ||||||
|  | 						return; | ||||||
|  | 					} | ||||||
|  | 				} catch (MalformedURLException e) { | ||||||
|  | 					//fall through; | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 			callback.failure("unable to request slot"); | ||||||
|  | 		}); | ||||||
|  | 		Log.d(Config.LOGTAG, "requesting slot with p1. md5=" + md5); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 	public interface OnSlotRequested { | ||||||
|  | 
 | ||||||
|  | 		void success(Slot slot); | ||||||
|  | 
 | ||||||
|  | 		void failure(String message); | ||||||
|  | 
 | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	public static class Slot { | ||||||
|  | 		private final URL putUrl; | ||||||
|  | 		private URL getUrl; | ||||||
|  | 		private HashMap<String, String> headers; | ||||||
|  | 
 | ||||||
|  | 		private Slot(URL putUrl) { | ||||||
|  | 			this.putUrl = putUrl; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		public URL getPutUrl() { | ||||||
|  | 			return putUrl; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		public URL getGetUrl() { | ||||||
|  | 			return getUrl; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		public HashMap<String, String> getHeaders() { | ||||||
|  | 			return headers; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @ -68,7 +68,7 @@ public class AbstractConnectionManager { | |||||||
| 		is = new FileInputStream(file); | 		is = new FileInputStream(file); | ||||||
| 		size = (int) file.getSize(); | 		size = (int) file.getSize(); | ||||||
| 		if (file.getKey() == null) { | 		if (file.getKey() == null) { | ||||||
| 			return new Pair<InputStream,Integer>(is,size); | 			return new Pair<>(is,size); | ||||||
| 		} | 		} | ||||||
| 		try { | 		try { | ||||||
| 			if (gcm) { | 			if (gcm) { | ||||||
| @ -81,16 +81,10 @@ public class AbstractConnectionManager { | |||||||
| 				Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); | 				Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); | ||||||
| 				cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(file.getKey(), "AES"), ips); | 				cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(file.getKey(), "AES"), ips); | ||||||
| 				Log.d(Config.LOGTAG, "opening encrypted input stream"); | 				Log.d(Config.LOGTAG, "opening encrypted input stream"); | ||||||
| 				return new Pair<InputStream,Integer>(new CipherInputStream(is, cipher),(size / 16 + 1) * 16); | 				return new Pair<>(new CipherInputStream(is, cipher),(size / 16 + 1) * 16); | ||||||
| 			} | 			} | ||||||
| 		} catch (InvalidKeyException e) { | 		} catch (Exception e) { | ||||||
| 			return null; | 			throw new AssertionError(e); | ||||||
| 		} catch (NoSuchAlgorithmException e) { |  | ||||||
| 			return null; |  | ||||||
| 		} catch (NoSuchPaddingException e) { |  | ||||||
| 			return null; |  | ||||||
| 		} catch (InvalidAlgorithmParameterException e) { |  | ||||||
| 			return null; |  | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -88,7 +88,7 @@ import eu.siacs.conversations.generator.IqGenerator; | |||||||
| import eu.siacs.conversations.generator.MessageGenerator; | import eu.siacs.conversations.generator.MessageGenerator; | ||||||
| import eu.siacs.conversations.generator.PresenceGenerator; | import eu.siacs.conversations.generator.PresenceGenerator; | ||||||
| import eu.siacs.conversations.http.HttpConnectionManager; | import eu.siacs.conversations.http.HttpConnectionManager; | ||||||
| import eu.siacs.conversations.http.AesGcmURLStreamHandlerFactory; | import eu.siacs.conversations.http.CustomURLStreamHandlerFactory; | ||||||
| import eu.siacs.conversations.parser.AbstractParser; | import eu.siacs.conversations.parser.AbstractParser; | ||||||
| import eu.siacs.conversations.parser.IqParser; | import eu.siacs.conversations.parser.IqParser; | ||||||
| import eu.siacs.conversations.parser.MessageParser; | import eu.siacs.conversations.parser.MessageParser; | ||||||
| @ -152,7 +152,7 @@ public class XmppConnectionService extends Service { | |||||||
| 	private static final String ACTION_MERGE_PHONE_CONTACTS = "merge_phone_contacts"; | 	private static final String ACTION_MERGE_PHONE_CONTACTS = "merge_phone_contacts"; | ||||||
| 
 | 
 | ||||||
| 	static { | 	static { | ||||||
| 		URL.setURLStreamHandlerFactory(new AesGcmURLStreamHandlerFactory()); | 		URL.setURLStreamHandlerFactory(new CustomURLStreamHandlerFactory()); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	public final CountDownLatch restoredFromDatabaseLatch = new CountDownLatch(1); | 	public final CountDownLatch restoredFromDatabaseLatch = new CountDownLatch(1); | ||||||
|  | |||||||
							
								
								
									
										60
									
								
								src/main/java/eu/siacs/conversations/utils/Checksum.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								src/main/java/eu/siacs/conversations/utils/Checksum.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,60 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright (c) 2018, Daniel Gultsch All rights reserved. | ||||||
|  |  * | ||||||
|  |  * Redistribution and use in source and binary forms, with or without modification, | ||||||
|  |  * are permitted provided that the following conditions are met: | ||||||
|  |  * | ||||||
|  |  * 1. Redistributions of source code must retain the above copyright notice, this | ||||||
|  |  * list of conditions and the following disclaimer. | ||||||
|  |  * | ||||||
|  |  * 2. Redistributions in binary form must reproduce the above copyright notice, | ||||||
|  |  * this list of conditions and the following disclaimer in the documentation and/or | ||||||
|  |  * other materials provided with the distribution. | ||||||
|  |  * | ||||||
|  |  * 3. Neither the name of the copyright holder nor the names of its contributors | ||||||
|  |  * may be used to endorse or promote products derived from this software without | ||||||
|  |  * specific prior written permission. | ||||||
|  |  * | ||||||
|  |  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND | ||||||
|  |  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||||||
|  |  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||||||
|  |  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR | ||||||
|  |  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||||||
|  |  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||||||
|  |  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON | ||||||
|  |  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||||
|  |  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||||||
|  |  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package eu.siacs.conversations.utils; | ||||||
|  | 
 | ||||||
|  | import android.util.Base64; | ||||||
|  | 
 | ||||||
|  | import java.io.IOException; | ||||||
|  | import java.io.InputStream; | ||||||
|  | import java.security.MessageDigest; | ||||||
|  | import java.security.NoSuchAlgorithmException; | ||||||
|  | 
 | ||||||
|  | public class Checksum { | ||||||
|  | 
 | ||||||
|  | 	public static String md5(InputStream inputStream) throws IOException { | ||||||
|  | 		byte[] buffer = new byte[4096]; | ||||||
|  | 		MessageDigest messageDigest; | ||||||
|  | 		try { | ||||||
|  | 			messageDigest = MessageDigest.getInstance("MD5"); | ||||||
|  | 		} catch (NoSuchAlgorithmException e) { | ||||||
|  | 			throw new AssertionError(e); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		int count; | ||||||
|  | 		do { | ||||||
|  | 			count = inputStream.read(buffer); | ||||||
|  | 			if (count > 0) { | ||||||
|  | 				messageDigest.update(buffer, 0, count); | ||||||
|  | 			} | ||||||
|  | 		} while (count != -1); | ||||||
|  | 		inputStream.close(); | ||||||
|  | 		return Base64.encodeToString(messageDigest.digest(), Base64.NO_WRAP); | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @ -20,4 +20,5 @@ public final class Namespace { | |||||||
| 	public static final String NICK = "http://jabber.org/protocol/nick"; | 	public static final String NICK = "http://jabber.org/protocol/nick"; | ||||||
| 	public static final String FLEXIBLE_OFFLINE_MESSAGE_RETRIEVAL = "http://jabber.org/protocol/offline"; | 	public static final String FLEXIBLE_OFFLINE_MESSAGE_RETRIEVAL = "http://jabber.org/protocol/offline"; | ||||||
| 	public static final String BIND = "urn:ietf:params:xml:ns:xmpp-bind"; | 	public static final String BIND = "urn:ietf:params:xml:ns:xmpp-bind"; | ||||||
|  | 	public static final String P1_S3_FILE_TRANSFER = "p1:s3filetransfer"; | ||||||
| } | } | ||||||
|  | |||||||
| @ -1807,6 +1807,10 @@ public class XmppConnection implements Runnable { | |||||||
| 			this.blockListRequested = value; | 			this.blockListRequested = value; | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
|  | 		public boolean p1S3FileTransfer() { | ||||||
|  | 			return hasDiscoFeature(Jid.of(account.getServer()),Namespace.P1_S3_FILE_TRANSFER); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
| 		public boolean httpUpload(long filesize) { | 		public boolean httpUpload(long filesize) { | ||||||
| 			if (Config.DISABLE_HTTP_UPLOAD) { | 			if (Config.DISABLE_HTTP_UPLOAD) { | ||||||
| 				return false; | 				return false; | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 Daniel Gultsch
						Daniel Gultsch