handle p1s3 urls in http downloader

This commit is contained in:
Daniel Gultsch 2018-05-25 15:37:14 +02:00
parent 4626bdf8d8
commit ea5cdec186
3 changed files with 134 additions and 64 deletions

View File

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

View File

@ -106,7 +106,8 @@ public class MessageGenerator extends AbstractGenerator {
final URL url = fileParams.url; final URL url = fileParams.url;
if (P1S3UrlStreamHandler.PROTOCOL_NAME.equals(url.getProtocol())) { if (P1S3UrlStreamHandler.PROTOCOL_NAME.equals(url.getProtocol())) {
Element x = packet.addChild("x", Namespace.P1_S3_FILE_TRANSFER); 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()); x.setAttribute("fileid", url.getHost());
return packet; return packet;
} else { } else {
@ -127,7 +128,8 @@ public class MessageGenerator extends AbstractGenerator {
final URL url = fileParams.url; final URL url = fileParams.url;
if (P1S3UrlStreamHandler.PROTOCOL_NAME.equals(url.getProtocol())) { if (P1S3UrlStreamHandler.PROTOCOL_NAME.equals(url.getProtocol())) {
Element x = packet.addChild("x", Namespace.P1_S3_FILE_TRANSFER); 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()); x.setAttribute("fileid", url.getHost());
} else { } else {
packet.setBody(url.toString()); packet.setBody(url.toString());

View File

@ -1,6 +1,7 @@
package eu.siacs.conversations.http; package eu.siacs.conversations.http;
import android.os.PowerManager; import android.os.PowerManager;
import android.support.annotation.Nullable;
import android.util.Log; import android.util.Log;
import java.io.BufferedInputStream; import java.io.BufferedInputStream;
@ -17,6 +18,7 @@ import javax.net.ssl.SSLHandshakeException;
import eu.siacs.conversations.Config; import eu.siacs.conversations.Config;
import eu.siacs.conversations.R; import eu.siacs.conversations.R;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.DownloadableFile; import eu.siacs.conversations.entities.DownloadableFile;
import eu.siacs.conversations.entities.Message; import eu.siacs.conversations.entities.Message;
import eu.siacs.conversations.entities.Transferable; 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.CryptoHelper;
import eu.siacs.conversations.utils.FileWriterException; import eu.siacs.conversations.utils.FileWriterException;
import eu.siacs.conversations.utils.WakeLockHelper; 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 { public class HttpDownloadConnection implements Transferable {
@ -39,8 +44,9 @@ public class HttpDownloadConnection implements Transferable {
private int mStatus = Transferable.STATUS_UNKNOWN; private int mStatus = Transferable.STATUS_UNKNOWN;
private boolean acceptedAutomatically = false; private boolean acceptedAutomatically = false;
private int mProgress = 0; private int mProgress = 0;
private boolean mUseTor = false; private final boolean mUseTor;
private boolean canceled = false; private boolean canceled = false;
private Method method = Method.HTTP_UPLOAD;
public HttpDownloadConnection(HttpConnectionManager manager) { public HttpDownloadConnection(HttpConnectionManager manager) {
this.mHttpConnectionManager = manager; this.mHttpConnectionManager = manager;
@ -100,6 +106,7 @@ public class HttpDownloadConnection implements Transferable {
if (this.message.getEncryption() == Message.ENCRYPTION_AXOLOTL && this.file.getKey() == null) { if (this.message.getEncryption() == Message.ENCRYPTION_AXOLOTL && this.file.getKey() == null) {
this.message.setEncryption(Message.ENCRYPTION_NONE); this.message.setEncryption(Message.ENCRYPTION_NONE);
} }
method = mUrl.getProtocol().equalsIgnoreCase(P1S3UrlStreamHandler.PROTOCOL_NAME) ? Method.P1_S3 : Method.HTTP_UPLOAD;
checkFileSize(interactive); checkFileSize(interactive);
} catch (MalformedURLException e) { } catch (MalformedURLException e) {
this.cancel(); 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; this.mProgress = (int) i;
mHttpConnectionManager.updateConversationUi(false); mHttpConnectionManager.updateConversationUi(false);
} }
@ -179,27 +186,64 @@ public class HttpDownloadConnection implements Transferable {
private class FileSizeChecker implements Runnable { private class FileSizeChecker implements Runnable {
private boolean interactive = false; private final boolean interactive;
public FileSizeChecker(boolean interactive) { FileSizeChecker(boolean interactive) {
this.interactive = interactive; this.interactive = interactive;
} }
@Override @Override
public void run() { 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; long size;
try { try {
size = retrieveFileSize(); size = retrieveFileSize();
} catch (Exception e) { } catch (Exception e) {
changeStatus(STATUS_OFFER_CHECK_FILESIZE);
Log.d(Config.LOGTAG, "io exception in http file size checker: " + e.getMessage()); Log.d(Config.LOGTAG, "io exception in http file size checker: " + e.getMessage());
if (interactive) { retrieveFailed(e);
showToastForException(e);
} else {
HttpDownloadConnection.this.acceptedAutomatically = false;
HttpDownloadConnection.this.mXmppConnectionService.getNotificationService().push(message);
}
cancel();
return; return;
} }
file.setExpectedSize(size); file.setExpectedSize(size);
@ -226,10 +270,14 @@ public class HttpDownloadConnection implements Transferable {
} else { } else {
connection = (HttpURLConnection) mUrl.openConnection(); 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); connection.setUseCaches(false);
Log.d(Config.LOGTAG, "url: " + connection.getURL().toString()); Log.d(Config.LOGTAG, "url: " + connection.getURL().toString());
Log.d(Config.LOGTAG, "connection: " + connection.toString());
connection.setRequestProperty("User-Agent", mXmppConnectionService.getIqGenerator().getIdentityName()); connection.setRequestProperty("User-Agent", mXmppConnectionService.getIqGenerator().getIdentityName());
if (connection instanceof HttpsURLConnection) { if (connection instanceof HttpsURLConnection) {
mHttpConnectionManager.setupTrustManager((HttpsURLConnection) connection, interactive); mHttpConnectionManager.setupTrustManager((HttpsURLConnection) connection, interactive);
@ -237,7 +285,18 @@ public class HttpDownloadConnection implements Transferable {
connection.setConnectTimeout(Config.SOCKET_TIMEOUT * 1000); connection.setConnectTimeout(Config.SOCKET_TIMEOUT * 1000);
connection.setReadTimeout(Config.SOCKET_TIMEOUT * 1000); connection.setReadTimeout(Config.SOCKET_TIMEOUT * 1000);
connection.connect(); 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(); connection.disconnect();
if (contentLength == null) { if (contentLength == null) {
throw new IOException("no content-length found in HEAD response"); 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 class FileDownloader implements Runnable {
private boolean interactive = false; private final boolean interactive;
private OutputStream os; private OutputStream os;
@ -374,7 +433,9 @@ public class HttpDownloadConnection implements Transferable {
message.setType(Message.TYPE_FILE); message.setType(Message.TYPE_FILE);
final URL url; final URL url;
final String ref = mUrl.getRef(); 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); url = CryptoHelper.toAesGcmUrl(mUrl);
} else { } else {
url = mUrl; url = mUrl;