handle p1s3 urls in http downloader

Daniel Gultsch created

Change summary

src/main/java/eu/siacs/conversations/generator/IqGenerator.java       | 97 
src/main/java/eu/siacs/conversations/generator/MessageGenerator.java  |  6 
src/main/java/eu/siacs/conversations/http/HttpDownloadConnection.java | 95 
3 files changed, 134 insertions(+), 64 deletions(-)

Detailed changes

src/main/java/eu/siacs/conversations/generator/IqGenerator.java 🔗

@@ -64,7 +64,7 @@ public class IqGenerator extends AbstractGenerator {
 		query.addChild("version").setContent(getIdentityVersion());
 		if ("chromium".equals(android.os.Build.BRAND)) {
 			query.addChild("os").setContent("Chrome OS");
-		} else{
+		} else {
 			query.addChild("os").setContent("Android");
 		}
 		return packet;
@@ -72,7 +72,7 @@ public class IqGenerator extends AbstractGenerator {
 
 	public IqPacket entityTimeResponse(IqPacket request) {
 		final IqPacket packet = request.generateResponse(IqPacket.TYPE.RESULT);
-		Element time = packet.addChild("time","urn:xmpp:time");
+		Element time = packet.addChild("time", "urn:xmpp:time");
 		final long now = System.currentTimeMillis();
 		time.addChild("utc").setContent(getTimestamp(now));
 		TimeZone ourTimezone = TimeZone.getDefault();
@@ -80,25 +80,25 @@ public class IqGenerator extends AbstractGenerator {
 		long offsetMinutes = Math.abs((offsetSeconds % 3600) / 60);
 		long offsetHours = offsetSeconds / 3600;
 		String hours;
-		if (offsetHours<0) {
-			hours = String.format(Locale.US,"%03d",offsetHours);
+		if (offsetHours < 0) {
+			hours = String.format(Locale.US, "%03d", offsetHours);
 		} else {
-			hours = String.format(Locale.US,"%02d",offsetHours);
+			hours = String.format(Locale.US, "%02d", offsetHours);
 		}
-		String minutes = String.format(Locale.US,"%02d",offsetMinutes);
-		time.addChild("tzo").setContent(hours+":"+minutes);
+		String minutes = String.format(Locale.US, "%02d", offsetMinutes);
+		time.addChild("tzo").setContent(hours + ":" + minutes);
 		return packet;
 	}
 
 	public IqPacket purgeOfflineMessages() {
 		final IqPacket packet = new IqPacket(IqPacket.TYPE.SET);
-		packet.addChild("offline",Namespace.FLEXIBLE_OFFLINE_MESSAGE_RETRIEVAL).addChild("purge");
+		packet.addChild("offline", Namespace.FLEXIBLE_OFFLINE_MESSAGE_RETRIEVAL).addChild("purge");
 		return packet;
 	}
 
 	protected IqPacket publish(final String node, final Element item, final Bundle options) {
 		final IqPacket packet = new IqPacket(IqPacket.TYPE.SET);
-		final Element pubsub = packet.addChild("pubsub",Namespace.PUBSUB);
+		final Element pubsub = packet.addChild("pubsub", Namespace.PUBSUB);
 		final Element publish = pubsub.addChild("publish");
 		publish.setAttribute("node", node);
 		publish.addChild(item);
@@ -110,12 +110,12 @@ public class IqGenerator extends AbstractGenerator {
 	}
 
 	protected IqPacket publish(final String node, final Element item) {
-		return publish(node,item,null);
+		return publish(node, item, null);
 	}
 
 	protected IqPacket retrieve(String node, Element item) {
 		final IqPacket packet = new IqPacket(IqPacket.TYPE.GET);
-		final Element pubsub = packet.addChild("pubsub",Namespace.PUBSUB);
+		final Element pubsub = packet.addChild("pubsub", Namespace.PUBSUB);
 		final Element items = pubsub.addChild("items");
 		items.setAttribute("node", node);
 		if (item != null) {
@@ -126,7 +126,7 @@ public class IqGenerator extends AbstractGenerator {
 
 	public IqPacket publishNick(String nick) {
 		final Element item = new Element("item");
-		item.addChild("nick","http://jabber.org/protocol/nick").setContent(nick);
+		item.addChild("nick", "http://jabber.org/protocol/nick").setContent(nick);
 		return publish("http://jabber.org/protocol/nick", item);
 	}
 
@@ -142,7 +142,7 @@ public class IqGenerator extends AbstractGenerator {
 		final Element item = new Element("item");
 		item.setAttribute("id", avatar.sha1sum);
 		final Element metadata = item
-			.addChild("metadata", "urn:xmpp:avatar:metadata");
+				.addChild("metadata", "urn:xmpp:avatar:metadata");
 		final Element info = metadata.addChild("info");
 		info.setAttribute("bytes", avatar.size);
 		info.setAttribute("id", avatar.sha1sum);
@@ -177,20 +177,20 @@ public class IqGenerator extends AbstractGenerator {
 
 	public IqPacket retrieveDeviceIds(final Jid to) {
 		final IqPacket packet = retrieve(AxolotlService.PEP_DEVICE_LIST, null);
-		if(to != null) {
+		if (to != null) {
 			packet.setTo(to);
 		}
 		return packet;
 	}
 
 	public IqPacket retrieveBundlesForDevice(final Jid to, final int deviceid) {
-		final IqPacket packet = retrieve(AxolotlService.PEP_BUNDLES+":"+deviceid, null);
+		final IqPacket packet = retrieve(AxolotlService.PEP_BUNDLES + ":" + deviceid, null);
 		packet.setTo(to);
 		return packet;
 	}
 
 	public IqPacket retrieveVerificationForDevice(final Jid to, final int deviceid) {
-		final IqPacket packet = retrieve(AxolotlService.PEP_VERIFICATION+":"+deviceid, null);
+		final IqPacket packet = retrieve(AxolotlService.PEP_VERIFICATION + ":" + deviceid, null);
 		packet.setTo(to);
 		return packet;
 	}
@@ -198,7 +198,7 @@ public class IqGenerator extends AbstractGenerator {
 	public IqPacket publishDeviceIds(final Set<Integer> ids, final Bundle publishOptions) {
 		final Element item = new Element("item");
 		final Element list = item.addChild("list", AxolotlService.PEP_PREFIX);
-		for(Integer id:ids) {
+		for (Integer id : ids) {
 			final Element device = new Element("device");
 			device.setAttribute("id", id);
 			list.addChild(device);
@@ -207,43 +207,43 @@ public class IqGenerator extends AbstractGenerator {
 	}
 
 	public IqPacket publishBundles(final SignedPreKeyRecord signedPreKeyRecord, final IdentityKey identityKey,
-								   final Set<PreKeyRecord> preKeyRecords, final int deviceId, Bundle publishOptions) {
+	                               final Set<PreKeyRecord> preKeyRecords, final int deviceId, Bundle publishOptions) {
 		final Element item = new Element("item");
 		final Element bundle = item.addChild("bundle", AxolotlService.PEP_PREFIX);
 		final Element signedPreKeyPublic = bundle.addChild("signedPreKeyPublic");
 		signedPreKeyPublic.setAttribute("signedPreKeyId", signedPreKeyRecord.getId());
 		ECPublicKey publicKey = signedPreKeyRecord.getKeyPair().getPublicKey();
-		signedPreKeyPublic.setContent(Base64.encodeToString(publicKey.serialize(),Base64.DEFAULT));
+		signedPreKeyPublic.setContent(Base64.encodeToString(publicKey.serialize(), Base64.DEFAULT));
 		final Element signedPreKeySignature = bundle.addChild("signedPreKeySignature");
-		signedPreKeySignature.setContent(Base64.encodeToString(signedPreKeyRecord.getSignature(),Base64.DEFAULT));
+		signedPreKeySignature.setContent(Base64.encodeToString(signedPreKeyRecord.getSignature(), Base64.DEFAULT));
 		final Element identityKeyElement = bundle.addChild("identityKey");
 		identityKeyElement.setContent(Base64.encodeToString(identityKey.serialize(), Base64.DEFAULT));
 
 		final Element prekeys = bundle.addChild("prekeys", AxolotlService.PEP_PREFIX);
-		for(PreKeyRecord preKeyRecord:preKeyRecords) {
+		for (PreKeyRecord preKeyRecord : preKeyRecords) {
 			final Element prekey = prekeys.addChild("preKeyPublic");
 			prekey.setAttribute("preKeyId", preKeyRecord.getId());
 			prekey.setContent(Base64.encodeToString(preKeyRecord.getKeyPair().getPublicKey().serialize(), Base64.DEFAULT));
 		}
 
-		return publish(AxolotlService.PEP_BUNDLES+":"+deviceId, item, publishOptions);
+		return publish(AxolotlService.PEP_BUNDLES + ":" + deviceId, item, publishOptions);
 	}
 
 	public IqPacket publishVerification(byte[] signature, X509Certificate[] certificates, final int deviceId) {
 		final Element item = new Element("item");
 		final Element verification = item.addChild("verification", AxolotlService.PEP_PREFIX);
 		final Element chain = verification.addChild("chain");
-		for(int i = 0; i < certificates.length; ++i) {
+		for (int i = 0; i < certificates.length; ++i) {
 			try {
 				Element certificate = chain.addChild("certificate");
 				certificate.setContent(Base64.encodeToString(certificates[i].getEncoded(), Base64.DEFAULT));
-				certificate.setAttribute("index",i);
+				certificate.setAttribute("index", i);
 			} catch (CertificateEncodingException e) {
 				Log.d(Config.LOGTAG, "could not encode certificate");
 			}
 		}
 		verification.addChild("signature").setContent(Base64.encodeToString(signature, Base64.DEFAULT));
-		return publish(AxolotlService.PEP_VERIFICATION+":"+deviceId, item);
+		return publish(AxolotlService.PEP_VERIFICATION + ":" + deviceId, item);
 	}
 
 	public IqPacket queryMessageArchiveManagement(final MessageArchiveService.Query mam) {
@@ -254,7 +254,7 @@ public class IqGenerator extends AbstractGenerator {
 		data.setFormType(mam.isLegacy() ? Namespace.MAM_LEGACY : Namespace.MAM);
 		if (mam.muc()) {
 			packet.setTo(mam.getWith());
-		} else if (mam.getWith()!=null) {
+		} else if (mam.getWith() != null) {
 			data.put("with", mam.getWith().toString());
 		}
 		final long start = mam.getStart();
@@ -276,6 +276,7 @@ public class IqGenerator extends AbstractGenerator {
 		set.addChild("max").setContent(String.valueOf(Config.PAGE_SIZE));
 		return packet;
 	}
+
 	public IqPacket generateGetBlockList() {
 		final IqPacket iq = new IqPacket(IqPacket.TYPE.GET);
 		iq.addChild("blocklist", Namespace.BLOCKING);
@@ -290,7 +291,7 @@ public class IqGenerator extends AbstractGenerator {
 		if (reportSpam) {
 			item.addChild("report", "urn:xmpp:reporting:0").addChild("spam");
 		}
-		Log.d(Config.LOGTAG,iq.toString());
+		Log.d(Config.LOGTAG, iq.toString());
 		return iq;
 	}
 
@@ -314,7 +315,7 @@ public class IqGenerator extends AbstractGenerator {
 	public IqPacket changeAffiliation(Conversation conference, Jid jid, String affiliation) {
 		List<Jid> jids = new ArrayList<>();
 		jids.add(jid);
-		return changeAffiliation(conference,jids,affiliation);
+		return changeAffiliation(conference, jids, affiliation);
 	}
 
 	public IqPacket changeAffiliation(Conversation conference, List<Jid> jids, String affiliation) {
@@ -322,7 +323,7 @@ public class IqGenerator extends AbstractGenerator {
 		packet.setTo(conference.getJid().asBareJid());
 		packet.setFrom(conference.getAccount().getJid());
 		Element query = packet.query("http://jabber.org/protocol/muc#admin");
-		for(Jid jid : jids) {
+		for (Jid jid : jids) {
 			Element item = query.addChild("item");
 			item.setAttribute("jid", jid.toString());
 			item.setAttribute("affiliation", affiliation);
@@ -344,17 +345,23 @@ public class IqGenerator extends AbstractGenerator {
 		IqPacket packet = new IqPacket(IqPacket.TYPE.GET);
 		packet.setTo(host);
 		Element request = packet.addChild("request", Namespace.HTTP_UPLOAD);
-		request.setAttribute("filename",convertFilename(file.getName()));
-		request.setAttribute("size",file.getExpectedSize());
-		request.setAttribute("content-type",mime);
+		request.setAttribute("filename", convertFilename(file.getName()));
+		request.setAttribute("size", file.getExpectedSize());
+		request.setAttribute("content-type", mime);
 		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());
+		packet.query(Namespace.P1_S3_FILE_TRANSFER).setAttribute("md5", md5);
+		return packet;
+	}
+
+	public IqPacket requestP1S3Url(Jid host, String fileId) {
+		IqPacket packet = new IqPacket(IqPacket.TYPE.GET);
+		packet.setTo(host);
+		packet.query(Namespace.P1_S3_FILE_TRANSFER).setAttribute("fileid", fileId);
 		return packet;
 	}
 
@@ -391,8 +398,8 @@ public class IqGenerator extends AbstractGenerator {
 		IqPacket packet = new IqPacket(IqPacket.TYPE.SET);
 		packet.setTo(appServer);
 		Element command = packet.addChild("command", "http://jabber.org/protocol/commands");
-		command.setAttribute("node","register-push-fcm");
-		command.setAttribute("action","execute");
+		command.setAttribute("node", "register-push-fcm");
+		command.setAttribute("action", "execute");
 		Data data = new Data();
 		data.put("token", token);
 		data.put("android-id", deviceId);
@@ -403,12 +410,12 @@ public class IqGenerator extends AbstractGenerator {
 
 	public IqPacket enablePush(Jid jid, String node, String secret) {
 		IqPacket packet = new IqPacket(IqPacket.TYPE.SET);
-		Element enable = packet.addChild("enable","urn:xmpp:push:0");
-		enable.setAttribute("jid",jid.toString());
+		Element enable = packet.addChild("enable", "urn:xmpp:push:0");
+		enable.setAttribute("jid", jid.toString());
 		enable.setAttribute("node", node);
 		Data data = new Data();
 		data.setFormType(Namespace.PUBSUB_PUBLISH_OPTIONS);
-		data.put("secret",secret);
+		data.put("secret", secret);
 		data.submit();
 		enable.addChild(data);
 		return packet;
@@ -417,7 +424,7 @@ public class IqGenerator extends AbstractGenerator {
 	public IqPacket queryAffiliation(Conversation conversation, String affiliation) {
 		IqPacket packet = new IqPacket(IqPacket.TYPE.GET);
 		packet.setTo(conversation.getJid().asBareJid());
-		packet.query("http://jabber.org/protocol/muc#admin").addChild("item").setAttribute("affiliation",affiliation);
+		packet.query("http://jabber.org/protocol/muc#admin").addChild("item").setAttribute("affiliation", affiliation);
 		return packet;
 	}
 
@@ -427,8 +434,8 @@ public class IqGenerator extends AbstractGenerator {
 		options.putString("muc#roomconfig_membersonly", "1");
 		options.putString("muc#roomconfig_publicroom", "0");
 		options.putString("muc#roomconfig_whois", "anyone");
-		options.putString("muc#roomconfig_enablearchiving","1");
-		options.putString("mam","1");
+		options.putString("muc#roomconfig_enablearchiving", "1");
+		options.putString("mam", "1");
 		return options;
 	}
 
@@ -437,14 +444,14 @@ public class IqGenerator extends AbstractGenerator {
 	}
 
 	public IqPacket publishPubsubConfiguration(Jid jid, String node, Data data) {
-		return pubsubConfiguration(jid,node,data);
+		return pubsubConfiguration(jid, node, data);
 	}
 
 	private IqPacket pubsubConfiguration(Jid jid, String node, Data data) {
 		IqPacket packet = new IqPacket(data == null ? IqPacket.TYPE.GET : IqPacket.TYPE.SET);
 		packet.setTo(jid);
-		Element pubsub = packet.addChild("pubsub","http://jabber.org/protocol/pubsub#owner");
-		Element configure = pubsub.addChild("configure").setAttribute("node",node);
+		Element pubsub = packet.addChild("pubsub", "http://jabber.org/protocol/pubsub#owner");
+		Element configure = pubsub.addChild("configure").setAttribute("node", node);
 		if (data != null) {
 			configure.addChild(data);
 		}

src/main/java/eu/siacs/conversations/generator/MessageGenerator.java 🔗

@@ -106,7 +106,8 @@ public class MessageGenerator extends AbstractGenerator {
 			final URL url = fileParams.url;
 			if (P1S3UrlStreamHandler.PROTOCOL_NAME.equals(url.getProtocol())) {
 				Element x = packet.addChild("x", Namespace.P1_S3_FILE_TRANSFER);
-				x.setAttribute("name", url.getFile());
+				final String file = url.getFile();
+				x.setAttribute("name", file.charAt(0) == '/' ? file.substring(1) : file);
 				x.setAttribute("fileid", url.getHost());
 				return packet;
 			} else {
@@ -127,7 +128,8 @@ public class MessageGenerator extends AbstractGenerator {
 			final URL url = fileParams.url;
 			if (P1S3UrlStreamHandler.PROTOCOL_NAME.equals(url.getProtocol())) {
 				Element x = packet.addChild("x", Namespace.P1_S3_FILE_TRANSFER);
-				x.setAttribute("name", url.getFile());
+				final String file = url.getFile();
+				x.setAttribute("name", file.charAt(0) == '/' ? file.substring(1) : file);
 				x.setAttribute("fileid", url.getHost());
 			} else {
 				packet.setBody(url.toString());

src/main/java/eu/siacs/conversations/http/HttpDownloadConnection.java 🔗

@@ -1,6 +1,7 @@
 package eu.siacs.conversations.http;
 
 import android.os.PowerManager;
+import android.support.annotation.Nullable;
 import android.util.Log;
 
 import java.io.BufferedInputStream;
@@ -17,6 +18,7 @@ import javax.net.ssl.SSLHandshakeException;
 
 import eu.siacs.conversations.Config;
 import eu.siacs.conversations.R;
+import eu.siacs.conversations.entities.Account;
 import eu.siacs.conversations.entities.DownloadableFile;
 import eu.siacs.conversations.entities.Message;
 import eu.siacs.conversations.entities.Transferable;
@@ -27,6 +29,9 @@ import eu.siacs.conversations.services.XmppConnectionService;
 import eu.siacs.conversations.utils.CryptoHelper;
 import eu.siacs.conversations.utils.FileWriterException;
 import eu.siacs.conversations.utils.WakeLockHelper;
+import eu.siacs.conversations.xmpp.OnIqPacketReceived;
+import eu.siacs.conversations.xmpp.stanzas.IqPacket;
+import rocks.xmpp.addr.Jid;
 
 public class HttpDownloadConnection implements Transferable {
 
@@ -39,8 +44,9 @@ public class HttpDownloadConnection implements Transferable {
 	private int mStatus = Transferable.STATUS_UNKNOWN;
 	private boolean acceptedAutomatically = false;
 	private int mProgress = 0;
-	private boolean mUseTor = false;
+	private final boolean mUseTor;
 	private boolean canceled = false;
+	private Method method = Method.HTTP_UPLOAD;
 
 	public HttpDownloadConnection(HttpConnectionManager manager) {
 		this.mHttpConnectionManager = manager;
@@ -100,6 +106,7 @@ public class HttpDownloadConnection implements Transferable {
 			if (this.message.getEncryption() == Message.ENCRYPTION_AXOLOTL && this.file.getKey() == null) {
 				this.message.setEncryption(Message.ENCRYPTION_NONE);
 			}
+			method = mUrl.getProtocol().equalsIgnoreCase(P1S3UrlStreamHandler.PROTOCOL_NAME) ? Method.P1_S3 : Method.HTTP_UPLOAD;
 			checkFileSize(interactive);
 		} catch (MalformedURLException e) {
 			this.cancel();
@@ -153,7 +160,7 @@ public class HttpDownloadConnection implements Transferable {
 		}
 	}
 
-	public void updateProgress(long i) {
+	private void updateProgress(long i) {
 		this.mProgress = (int) i;
 		mHttpConnectionManager.updateConversationUi(false);
 	}
@@ -179,27 +186,64 @@ public class HttpDownloadConnection implements Transferable {
 
 	private class FileSizeChecker implements Runnable {
 
-		private boolean interactive = false;
+		private final boolean interactive;
 
-		public FileSizeChecker(boolean interactive) {
+		FileSizeChecker(boolean interactive) {
 			this.interactive = interactive;
 		}
 
+
 		@Override
 		public void run() {
+			if (mUrl.getProtocol().equalsIgnoreCase(P1S3UrlStreamHandler.PROTOCOL_NAME)) {
+				retrieveUrl();
+			} else {
+				check();
+			}
+		}
+
+		private void retrieveUrl() {
+			changeStatus(STATUS_CHECKING);
+			final Account account = message.getConversation().getAccount();
+			IqPacket request = mXmppConnectionService.getIqGenerator().requestP1S3Url(Jid.of(account.getJid().getDomain()), mUrl.getHost());
+			mXmppConnectionService.sendIqPacket(message.getConversation().getAccount(), request, (a, packet) -> {
+				if (packet.getType() == IqPacket.TYPE.RESULT) {
+					String download = packet.query().getAttribute("download");
+					if (download != null) {
+						try {
+							mUrl = new URL(download);
+							check();
+							return;
+						} catch (MalformedURLException e) {
+							//fallthrough
+						}
+					}
+				}
+				Log.d(Config.LOGTAG,"unable to retrieve actual download url");
+				retrieveFailed(null);
+			});
+		}
+
+		private void retrieveFailed(@Nullable Exception e) {
+			changeStatus(STATUS_OFFER_CHECK_FILESIZE);
+			if (interactive) {
+				if (e != null) {
+					showToastForException(e);
+				}
+			} else {
+				HttpDownloadConnection.this.acceptedAutomatically = false;
+				HttpDownloadConnection.this.mXmppConnectionService.getNotificationService().push(message);
+			}
+			cancel();
+		}
+
+		private void check() {
 			long size;
 			try {
 				size = retrieveFileSize();
 			} catch (Exception e) {
-				changeStatus(STATUS_OFFER_CHECK_FILESIZE);
 				Log.d(Config.LOGTAG, "io exception in http file size checker: " + e.getMessage());
-				if (interactive) {
-					showToastForException(e);
-				} else {
-					HttpDownloadConnection.this.acceptedAutomatically = false;
-					HttpDownloadConnection.this.mXmppConnectionService.getNotificationService().push(message);
-				}
-				cancel();
+				retrieveFailed(e);
 				return;
 			}
 			file.setExpectedSize(size);
@@ -226,10 +270,14 @@ public class HttpDownloadConnection implements Transferable {
 				} else {
 					connection = (HttpURLConnection) mUrl.openConnection();
 				}
-				connection.setRequestMethod("HEAD");
+				if (method == Method.P1_S3) {
+					connection.setRequestMethod("GET");
+					connection.addRequestProperty("Range","bytes=0-0");
+				} else {
+					connection.setRequestMethod("HEAD");
+				}
 				connection.setUseCaches(false);
 				Log.d(Config.LOGTAG, "url: " + connection.getURL().toString());
-				Log.d(Config.LOGTAG, "connection: " + connection.toString());
 				connection.setRequestProperty("User-Agent", mXmppConnectionService.getIqGenerator().getIdentityName());
 				if (connection instanceof HttpsURLConnection) {
 					mHttpConnectionManager.setupTrustManager((HttpsURLConnection) connection, interactive);
@@ -237,7 +285,18 @@ public class HttpDownloadConnection implements Transferable {
 				connection.setConnectTimeout(Config.SOCKET_TIMEOUT * 1000);
 				connection.setReadTimeout(Config.SOCKET_TIMEOUT * 1000);
 				connection.connect();
-				String contentLength = connection.getHeaderField("Content-Length");
+				String contentLength;
+				if (method == Method.P1_S3) {
+					String contentRange = connection.getHeaderField("Content-Range");
+					String[] contentRangeParts = contentRange == null ? new String[0] : contentRange.split("/");
+					if (contentRangeParts.length != 2) {
+						contentLength = null;
+					} else {
+						contentLength = contentRangeParts[1];
+					}
+				} else {
+					contentLength = connection.getHeaderField("Content-Length");
+				}
 				connection.disconnect();
 				if (contentLength == null) {
 					throw new IOException("no content-length found in HEAD response");
@@ -255,7 +314,7 @@ public class HttpDownloadConnection implements Transferable {
 
 	private class FileDownloader implements Runnable {
 
-		private boolean interactive = false;
+		private final boolean interactive;
 
 		private OutputStream os;
 
@@ -374,7 +433,9 @@ public class HttpDownloadConnection implements Transferable {
 			message.setType(Message.TYPE_FILE);
 			final URL url;
 			final String ref = mUrl.getRef();
-			if (ref != null && AesGcmURLStreamHandler.IV_KEY.matcher(ref).matches()) {
+			if (method == Method.P1_S3) {
+				url = message.getFileParams().url;
+			} else if (ref != null && AesGcmURLStreamHandler.IV_KEY.matcher(ref).matches()) {
 				url = CryptoHelper.toAesGcmUrl(mUrl);
 			} else {
 				url = mUrl;