M src/cheogram/java/com/cheogram/android/BobTransfer.java => src/cheogram/java/com/cheogram/android/BobTransfer.java +50 -22
@@ 15,6 15,7 @@ import io.ipfs.cid.Cid;
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;
@@ 24,12 25,14 @@ import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.utils.CryptoHelper;
import eu.siacs.conversations.utils.MimeUtils;
import eu.siacs.conversations.xml.Element;
+import eu.siacs.conversations.xmpp.Jid;
import eu.siacs.conversations.xmpp.stanzas.IqPacket;
public class BobTransfer implements Transferable {
protected int status = Transferable.STATUS_OFFER;
- protected Message message;
protected URI uri;
+ protected Account account;
+ protected Jid to;
protected XmppConnectionService xmppConnectionService;
public static Cid cid(URI uri) {
@@ 44,10 47,15 @@ public class BobTransfer implements Transferable {
}
}
- public BobTransfer(Message message, XmppConnectionService xmppConnectionService) throws URISyntaxException {
- this.message = message;
+ public static URI uri(Cid cid) throws NoSuchAlgorithmException, URISyntaxException {
+ return new URI("cid", CryptoHelper.multihashAlgo(cid.getType()) + "+" + CryptoHelper.bytesToHex(cid.getHash()) + "@bob.xmpp.org", null);
+ }
+
+ public BobTransfer(URI uri, Account account, Jid to, XmppConnectionService xmppConnectionService) {
this.xmppConnectionService = xmppConnectionService;
- this.uri = new URI(message.getFileParams().url);
+ this.uri = uri;
+ this.to = to;
+ this.account = account;
}
@Override
@@ 56,10 64,7 @@ public class BobTransfer implements Transferable {
File f = xmppConnectionService.getFileForCid(cid(uri));
if (f != null && f.canRead()) {
- message.setRelativeFilePath(f.getAbsolutePath());
- finish();
- message.setTransferable(null);
- xmppConnectionService.updateConversationUi();
+ finish(f);
return true;
}
@@ 67,13 72,14 @@ public class BobTransfer implements Transferable {
changeStatus(Transferable.STATUS_DOWNLOADING);
IqPacket request = new IqPacket(IqPacket.TYPE.GET);
- request.setTo(message.getCounterpart());
+ request.setTo(to);
final Element dataq = request.addChild("data", "urn:xmpp:bob");
dataq.setAttribute("cid", uri.getSchemeSpecificPart());
- xmppConnectionService.sendIqPacket(message.getConversation().getAccount(), request, (acct, packet) -> {
+ xmppConnectionService.sendIqPacket(account, request, (acct, packet) -> {
final Element data = packet.findChild("data", "urn:xmpp:bob");
if (packet.getType() == IqPacket.TYPE.ERROR || data == null) {
Log.d(Config.LOGTAG, "BobTransfer failed: " + packet);
+ finish(null);
xmppConnectionService.showErrorToastInUi(R.string.download_failed_file_not_found);
} else {
final String contentType = data.getAttribute("type");
@@ 85,25 91,23 @@ public class BobTransfer implements Transferable {
try {
final byte[] bytes = Base64.decode(data.getContent(), Base64.DEFAULT);
- xmppConnectionService.getFileBackend().setupRelativeFilePath(message, new ByteArrayInputStream(bytes), fileExtension);
- DownloadableFile file = xmppConnectionService.getFileBackend().getFile(message);
+ File file = xmppConnectionService.getFileBackend().getStorageLocation(new ByteArrayInputStream(bytes), fileExtension);
file.getParentFile().mkdirs();
if (!file.exists() && !file.createNewFile()) {
throw new IOException(file.getAbsolutePath());
}
- final OutputStream outputStream = AbstractConnectionManager.createOutputStream(file, false, false);
+ final OutputStream outputStream = AbstractConnectionManager.createOutputStream(new DownloadableFile(file.getAbsolutePath()), false, false);
outputStream.write(bytes);
outputStream.flush();
outputStream.close();
- finish();
+ finish(file);
} catch (IOException e) {
+ finish(null);
xmppConnectionService.showErrorToastInUi(R.string.download_failed_could_not_write_file);
}
}
- message.setTransferable(null);
- xmppConnectionService.updateConversationUi();
});
return true;
} else {
@@ 130,7 134,6 @@ public class BobTransfer implements Transferable {
public void cancel() {
// No real way to cancel an iq in process...
changeStatus(Transferable.STATUS_CANCELLED);
- message.setTransferable(null);
}
protected void changeStatus(int newStatus) {
@@ 138,10 141,35 @@ public class BobTransfer implements Transferable {
xmppConnectionService.updateConversationUi();
}
- protected void finish() {
- final boolean privateMessage = message.isPrivateMessage();
- message.setType(privateMessage ? Message.TYPE_PRIVATE_FILE : Message.TYPE_FILE);
- xmppConnectionService.getFileBackend().updateFileParams(message, uri.toString(), false);
- xmppConnectionService.updateMessage(message);
+ protected void finish(File f) {
+ if (f != null) xmppConnectionService.updateConversationUi();
+ }
+
+ public static class ForMessage extends BobTransfer {
+ protected Message message;
+
+ public ForMessage(Message message, XmppConnectionService xmppConnectionService) throws URISyntaxException {
+ super(new URI(message.getFileParams().url), message.getConversation().getAccount(), message.getCounterpart(), xmppConnectionService);
+ this.message = message;
+ }
+
+ @Override
+ public void cancel() {
+ super.cancel();
+ message.setTransferable(null);
+ }
+
+ @Override
+ protected void finish(File f) {
+ if (f != null) {
+ message.setRelativeFilePath(f.getAbsolutePath());
+ final boolean privateMessage = message.isPrivateMessage();
+ message.setType(privateMessage ? Message.TYPE_PRIVATE_FILE : Message.TYPE_FILE);
+ xmppConnectionService.getFileBackend().updateFileParams(message, uri.toString(), false);
+ xmppConnectionService.updateMessage(message);
+ }
+ message.setTransferable(null);
+ super.finish(f);
+ }
}
}
M src/main/java/eu/siacs/conversations/entities/Contact.java => src/main/java/eu/siacs/conversations/entities/Contact.java +4 -0
@@ 414,6 414,10 @@ public class Contact implements ListItem, Blockable {
return ((this.subscription & (1 << option)) != 0);
}
+ public boolean canInferPresence() {
+ return showInContactList() || isSelf();
+ }
+
public boolean showInRoster() {
return (this.getOption(Contact.Options.IN_ROSTER) && (!this
.getOption(Contact.Options.DIRTY_DELETE)))
M src/main/java/eu/siacs/conversations/entities/Conversation.java => src/main/java/eu/siacs/conversations/entities/Conversation.java +6 -0
@@ 1130,6 1130,12 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
return count;
}
+ public boolean canInferPresence() {
+ final Contact contact = getContact();
+ if (contact != null && contact.canInferPresence()) return true;
+ return sentMessagesCount() > 0;
+ }
+
public boolean isWithStranger() {
final Contact contact = getContact();
return mode == MODE_SINGLE
M src/main/java/eu/siacs/conversations/entities/Conversational.java => src/main/java/eu/siacs/conversations/entities/Conversational.java +2 -0
@@ 45,4 45,6 @@ public interface Conversational {
int getMode();
String getUuid();
+
+ boolean canInferPresence();
}
M src/main/java/eu/siacs/conversations/entities/StubConversation.java => src/main/java/eu/siacs/conversations/entities/StubConversation.java +6 -0
@@ 70,4 70,10 @@ public class StubConversation implements Conversational {
public String getUuid() {
return uuid;
}
+
+ @Override
+ public boolean canInferPresence() {
+ final Contact contact = getContact();
+ return contact != null && contact.canInferPresence();
+ }
}
M src/main/java/eu/siacs/conversations/parser/MessageParser.java => src/main/java/eu/siacs/conversations/parser/MessageParser.java +1 -1
@@ 765,7 765,7 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece
if (message.trusted() && message.treatAsDownloadable() && manager.getAutoAcceptFileSize() > 0) {
if (message.getOob() != null && message.getOob().getScheme().equalsIgnoreCase("cid")) {
try {
- BobTransfer transfer = new BobTransfer(message, mXmppConnectionService);
+ BobTransfer transfer = new BobTransfer.ForMessage(message, mXmppConnectionService);
message.setTransferable(transfer);
transfer.start();
} catch (URISyntaxException e) {
M src/main/java/eu/siacs/conversations/persistance/FileBackend.java => src/main/java/eu/siacs/conversations/persistance/FileBackend.java +12 -7
@@ 887,13 887,7 @@ public class FileBackend {
}
public void setupRelativeFilePath(final Message message, final InputStream is, final String extension) throws IOException {
- Cid[] cids = calculateCids(is);
-
- setupRelativeFilePath(message, String.format("%s.%s", cids[0], extension));
- File file = getFile(message);
- for (int i = 0; i < cids.length; i++) {
- mXmppConnectionService.saveCid(cids[i], file);
- }
+ message.setRelativeFilePath(getStorageLocation(is, extension).getAbsolutePath());
}
public void setupRelativeFilePath(final Message message, final String filename) {
@@ 902,6 896,17 @@ public class FileBackend {
setupRelativeFilePath(message, filename, mime);
}
+ public File getStorageLocation(final InputStream is, final String extension) throws IOException {
+ final String mime = MimeUtils.guessMimeTypeFromExtension(extension);
+ Cid[] cids = calculateCids(is);
+
+ File file = getStorageLocation(String.format("%s.%s", cids[0], extension), mime);
+ for (int i = 0; i < cids.length; i++) {
+ mXmppConnectionService.saveCid(cids[i], file);
+ }
+ return file;
+ }
+
public File getStorageLocation(final String filename, final String mime) {
final File parentDirectory;
if (Strings.isNullOrEmpty(mime)) {
M src/main/java/eu/siacs/conversations/ui/ConversationFragment.java => src/main/java/eu/siacs/conversations/ui/ConversationFragment.java +1 -1
@@ 1946,7 1946,7 @@ public class ConversationFragment extends XmppFragment
}
if (message.getOob() != null && message.getOob().getScheme().equalsIgnoreCase("cid")) {
try {
- BobTransfer transfer = new BobTransfer(message, activity.xmppConnectionService);
+ BobTransfer transfer = new BobTransfer.ForMessage(message, activity.xmppConnectionService);
message.setTransferable(transfer);
transfer.start();
} catch (URISyntaxException e) {
M src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java => src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java +12 -1
@@ 36,10 36,14 @@ import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.core.content.res.ResourcesCompat;
+import com.cheogram.android.BobTransfer;
+
import com.google.common.base.Strings;
import java.io.IOException;
import java.net.URI;
+import java.net.URISyntaxException;
+import java.security.NoSuchAlgorithmException;
import java.util.List;
import java.util.Locale;
import java.util.regex.Matcher;
@@ 450,7 454,14 @@ public class MessageAdapter extends ArrayAdapter<Message> {
SpannableStringBuilder body = message.getMergedBody((cid) -> {
try {
DownloadableFile f = activity.xmppConnectionService.getFileForCid(cid);
- if (f == null) return null;
+ if (f == null || !f.canRead()) {
+ if (!message.trusted() && !message.getConversation().canInferPresence()) return null;
+
+ try {
+ new BobTransfer(BobTransfer.uri(cid), message.getConversation().getAccount(), message.getCounterpart(), activity.xmppConnectionService).start();
+ } catch (final NoSuchAlgorithmException | URISyntaxException e) { }
+ return null;
+ }
Drawable d = activity.xmppConnectionService.getFileBackend().getThumbnail(f, activity.getResources(), (int) (metrics.density * 288), true);
if (d == null) {
M src/main/java/eu/siacs/conversations/utils/CryptoHelper.java => src/main/java/eu/siacs/conversations/utils/CryptoHelper.java +13 -0
@@ 288,6 288,19 @@ public final class CryptoHelper {
return !u.contains(" ") && (u.startsWith("https://") || u.startsWith("http://") || u.startsWith("p1s3://")) && u.endsWith(".pgp");
}
+ public static String multihashAlgo(Multihash.Type type) throws NoSuchAlgorithmException {
+ switch(type) {
+ case sha1:
+ return "sha1";
+ case sha2_256:
+ return "sha-256";
+ case sha2_512:
+ return "sha-512";
+ default:
+ throw new NoSuchAlgorithmException("" + type);
+ }
+ }
+
public static Multihash.Type multihashType(String algo) throws NoSuchAlgorithmException {
if (algo.equals("SHA-1") || algo.equals("sha-1") || algo.equals("sha1")) {
return Multihash.Type.sha1;