aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/main/java/eu/siacs/conversations/entities/Bookmark.java4
-rw-r--r--src/main/java/eu/siacs/conversations/generator/IqGenerator.java18
-rw-r--r--src/main/java/eu/siacs/conversations/parser/MessageParser.java66
-rw-r--r--src/main/java/eu/siacs/conversations/services/MessageArchiveService.java137
-rw-r--r--src/main/java/eu/siacs/conversations/services/XmppConnectionService.java30
-rw-r--r--src/main/java/eu/siacs/conversations/xml/Element.java5
-rw-r--r--src/main/java/eu/siacs/conversations/xmpp/OnAdvancedStreamFeaturesAvailable.java7
-rw-r--r--src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java8
-rw-r--r--src/main/java/eu/siacs/conversations/xmpp/forms/Data.java10
9 files changed, 277 insertions, 8 deletions
diff --git a/src/main/java/eu/siacs/conversations/entities/Bookmark.java b/src/main/java/eu/siacs/conversations/entities/Bookmark.java
index 559e2f2d..70d852fe 100644
--- a/src/main/java/eu/siacs/conversations/entities/Bookmark.java
+++ b/src/main/java/eu/siacs/conversations/entities/Bookmark.java
@@ -102,9 +102,7 @@ public class Bookmark extends Element implements ListItem {
}
public boolean autojoin() {
- String autojoin = this.getAttribute("autojoin");
- return (autojoin != null && (autojoin.equalsIgnoreCase("true") || autojoin
- .equalsIgnoreCase("1")));
+ return this.getAttributeAsBoolean("autojoin");
}
public String getPassword() {
diff --git a/src/main/java/eu/siacs/conversations/generator/IqGenerator.java b/src/main/java/eu/siacs/conversations/generator/IqGenerator.java
index 5d674748..56a0776f 100644
--- a/src/main/java/eu/siacs/conversations/generator/IqGenerator.java
+++ b/src/main/java/eu/siacs/conversations/generator/IqGenerator.java
@@ -4,8 +4,10 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import eu.siacs.conversations.services.MessageArchiveService;
import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.xml.Element;
+import eu.siacs.conversations.xmpp.forms.Data;
import eu.siacs.conversations.xmpp.jid.Jid;
import eu.siacs.conversations.xmpp.pep.Avatar;
import eu.siacs.conversations.xmpp.stanzas.IqPacket;
@@ -94,4 +96,20 @@ public class IqGenerator extends AbstractGenerator {
}
return packet;
}
+
+ public IqPacket queryMessageArchiveManagement(MessageArchiveService.Query mam) {
+ final IqPacket packet = new IqPacket(IqPacket.TYPE_SET);
+ Element query = packet.query("urn:xmpp:mam:0");
+ query.setAttribute("queryid",mam.getQueryId());
+ Data data = new Data();
+ data.setFormType("urn:xmpp:mam:0");
+ data.put("with",mam.getWith().toString());
+ data.put("start",getTimestamp(mam.getStart()));
+ data.put("end",getTimestamp(mam.getEnd()));
+ query.addChild(data);
+ if (mam.getAfter() != null) {
+ query.addChild("set", "http://jabber.org/protocol/rsm").addChild("after").setContent(mam.getAfter());
+ }
+ return packet;
+ }
}
diff --git a/src/main/java/eu/siacs/conversations/parser/MessageParser.java b/src/main/java/eu/siacs/conversations/parser/MessageParser.java
index fd9e1b6c..b902db51 100644
--- a/src/main/java/eu/siacs/conversations/parser/MessageParser.java
+++ b/src/main/java/eu/siacs/conversations/parser/MessageParser.java
@@ -272,6 +272,58 @@ public class MessageParser extends AbstractParser implements
return finishedMessage;
}
+ private Message parseMamMessage(MessagePacket packet, final Account account) {
+ final Element result = packet.findChild("result","urn:xmpp:mam:0");
+ if (result == null ) {
+ return null;
+ }
+ final Element forwarded = result.findChild("forwarded","urn:xmpp:forward:0");
+ if (forwarded == null) {
+ return null;
+ }
+ final Element message = forwarded.findChild("message");
+ if (message == null) {
+ return null;
+ }
+ final Element body = message.findChild("body");
+ if (body == null || message.hasChild("private","urn:xmpp:carbons:2") || message.hasChild("no-copy","urn:xmpp:hints")) {
+ return null;
+ }
+ int encryption;
+ String content = getPgpBody(message);
+ if (content != null) {
+ encryption = Message.ENCRYPTION_PGP;
+ } else {
+ encryption = Message.ENCRYPTION_NONE;
+ content = body.getContent();
+ }
+ if (content == null) {
+ return null;
+ }
+ final long timestamp = getTimestamp(forwarded);
+ final Jid to = message.getAttributeAsJid("to");
+ final Jid from = message.getAttributeAsJid("from");
+ Jid counterpart;
+ int status;
+ Conversation conversation;
+ if (from!=null && to != null && from.toBareJid().equals(account.getJid().toBareJid())) {
+ status = Message.STATUS_SEND;
+ conversation = this.mXmppConnectionService.findOrCreateConversation(account,to.toBareJid(),false);
+ counterpart = to;
+ } else if (from !=null && to != null) {
+ status = Message.STATUS_RECEIVED;
+ conversation = this.mXmppConnectionService.findOrCreateConversation(account,from.toBareJid(),false);
+ counterpart = from;
+ } else {
+ return null;
+ }
+ Message finishedMessage = new Message(conversation,content,encryption,status);
+ finishedMessage.setTime(timestamp);
+ finishedMessage.setCounterpart(counterpart);
+ Log.d(Config.LOGTAG,"received mam message "+content);
+ return finishedMessage;
+ }
+
private void parseError(final MessagePacket packet, final Account account) {
final Jid from = packet.getFrom();
mXmppConnectionService.markMessage(account, from.toBareJid(),
@@ -445,6 +497,17 @@ public class MessageParser extends AbstractParser implements
message.markUnread();
}
}
+ } else if (packet.hasChild("result","urn:xmpp:mam:0")) {
+ message = parseMamMessage(packet, account);
+ if (message != null) {
+ Conversation conversation = message.getConversation();
+ conversation.add(message);
+ mXmppConnectionService.databaseBackend.createMessage(message);
+ }
+ return;
+ } else if (packet.hasChild("fin","urn:xmpp:mam:0")) {
+ Element fin = packet.findChild("fin","urn:xmpp:mam:0");
+ mXmppConnectionService.getMessageArchiveService().processFin(fin);
} else {
parseNonMessage(packet, account);
}
@@ -493,7 +556,6 @@ public class MessageParser extends AbstractParser implements
&& conversation.getOtrSession() != null
&& !conversation.getOtrSession().getSessionID().getUserID()
.equals(message.getCounterpart().getResourcepart())) {
- Log.d(Config.LOGTAG, "ending because of reasons");
conversation.endOtrIfNeeded();
}
@@ -506,7 +568,7 @@ public class MessageParser extends AbstractParser implements
if (message.trusted() && message.bodyContainsDownloadable()) {
this.mXmppConnectionService.getHttpConnectionManager()
.createNewConnection(message);
- } else {
+ } else if (!message.isRead()) {
mXmppConnectionService.getNotificationService().push(message);
}
mXmppConnectionService.updateConversationUi();
diff --git a/src/main/java/eu/siacs/conversations/services/MessageArchiveService.java b/src/main/java/eu/siacs/conversations/services/MessageArchiveService.java
new file mode 100644
index 00000000..4f47cdbe
--- /dev/null
+++ b/src/main/java/eu/siacs/conversations/services/MessageArchiveService.java
@@ -0,0 +1,137 @@
+package eu.siacs.conversations.services;
+
+import android.util.Log;
+
+import java.math.BigInteger;
+import java.util.HashSet;
+
+import eu.siacs.conversations.Config;
+import eu.siacs.conversations.entities.Account;
+import eu.siacs.conversations.entities.Conversation;
+import eu.siacs.conversations.xml.Element;
+import eu.siacs.conversations.xmpp.OnIqPacketReceived;
+import eu.siacs.conversations.xmpp.jid.Jid;
+import eu.siacs.conversations.xmpp.stanzas.IqPacket;
+
+public class MessageArchiveService {
+
+ private final XmppConnectionService mXmppConnectionService;
+
+ private final HashSet<Query> queries = new HashSet<Query>();
+
+ public MessageArchiveService(final XmppConnectionService service) {
+ this.mXmppConnectionService = service;
+ }
+
+ public void query(final Conversation conversation) {
+ synchronized (this.queries) {
+ final Account account = conversation.getAccount();
+ long start = conversation.getLastMessageReceived();
+ long end = account.getXmppConnection().getLastSessionEstablished();
+ final Query query = new Query(conversation, start, end);
+ this.queries.add(query);
+ IqPacket packet = this.mXmppConnectionService.getIqGenerator().queryMessageArchiveManagement(query);
+ this.mXmppConnectionService.sendIqPacket(account, packet, new OnIqPacketReceived() {
+ @Override
+ public void onIqPacketReceived(Account account, IqPacket packet) {
+ Log.d(Config.LOGTAG, packet.toString());
+ }
+ });
+ }
+ }
+
+ public void processFin(Element fin) {
+ if (fin == null) {
+ return;
+ }
+ Query query = findQuery(fin.getAttribute("queryid"));
+ if (query == null) {
+ return;
+ }
+ Log.d(Config.LOGTAG,"fin "+fin.toString());
+ boolean complete = fin.getAttributeAsBoolean("complete");
+ Element set = fin.findChild("set","http://jabber.org/protocol/rsm");
+ Element last = set == null ? null : set.findChild("last");
+ if (complete || last == null) {
+ Log.d(Config.LOGTAG,"completed mam query for "+query.getWith().toString());
+ synchronized (this.queries) {
+ this.queries.remove(query);
+ }
+ } else {
+ Query nextQuery = query.next(last == null ? null : last.getContent());
+ IqPacket packet = this.mXmppConnectionService.getIqGenerator().queryMessageArchiveManagement(nextQuery);
+ synchronized (this.queries) {
+ this.queries.remove(query);
+ this.queries.add(nextQuery);
+ }
+ Log.d(Config.LOGTAG,packet.toString());
+ this.mXmppConnectionService.sendIqPacket(query.getConversation().getAccount(),packet,new OnIqPacketReceived() {
+ @Override
+ public void onIqPacketReceived(Account account, IqPacket packet) {
+ Log.d(Config.LOGTAG,packet.toString());
+ }
+ });
+ }
+ }
+
+ private Query findQuery(String id) {
+ if (id == null) {
+ return null;
+ }
+ synchronized (this.queries) {
+ for(Query query : this.queries) {
+ if (query.getQueryId().equals(id)) {
+ return query;
+ }
+ }
+ return null;
+ }
+ }
+
+ public class Query {
+ private long start;
+ private long end;
+ private Jid with;
+ private String queryId;
+ private String after = null;
+ private Conversation conversation;
+
+ public Query(Conversation conversation, long start, long end) {
+ this.conversation = conversation;
+ this.with = conversation.getContactJid().toBareJid();
+ this.start = start;
+ this.end = end;
+ this.queryId = new BigInteger(50, mXmppConnectionService.getRNG()).toString(32);
+ }
+
+ public Query next(String after) {
+ Query query = new Query(this.conversation,this.start,this.end);
+ query.after = after;
+ return query;
+ }
+
+ public String getAfter() {
+ return after;
+ }
+
+ public String getQueryId() {
+ return queryId;
+ }
+
+ public Jid getWith() {
+ return with;
+ }
+
+ public long getStart() {
+ return start;
+ }
+
+ public long getEnd() {
+ return end;
+ }
+
+ public Conversation getConversation() {
+ return conversation;
+ }
+ }
+}
diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java
index b9da4a81..9d73868c 100644
--- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java
+++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java
@@ -73,6 +73,7 @@ import eu.siacs.conversations.utils.OnPhoneContactsLoadedListener;
import eu.siacs.conversations.utils.PRNGFixes;
import eu.siacs.conversations.utils.PhoneHelper;
import eu.siacs.conversations.xml.Element;
+import eu.siacs.conversations.xmpp.OnAdvancedStreamFeaturesAvailable;
import eu.siacs.conversations.xmpp.OnBindListener;
import eu.siacs.conversations.xmpp.OnContactStatusChanged;
import eu.siacs.conversations.xmpp.OnIqPacketReceived;
@@ -141,6 +142,7 @@ public class XmppConnectionService extends Service {
private HttpConnectionManager mHttpConnectionManager = new HttpConnectionManager(
this);
private AvatarService mAvatarService = new AvatarService(this);
+ private MessageArchiveService mMessageArchiveService = new MessageArchiveService(this);
private OnConversationUpdate mOnConversationUpdate = null;
private Integer convChangedListenerCount = 0;
private OnAccountUpdate mOnAccountUpdate = null;
@@ -203,6 +205,12 @@ public class XmppConnectionService extends Service {
getNotificationService().updateErrorNotification();
}
};
+ private OnAdvancedStreamFeaturesAvailable onAdvancedStreamFeaturesAvailable = new OnAdvancedStreamFeaturesAvailable() {
+ @Override
+ public void onAdvancedStreamFeaturesAvailable(Account account) {
+ queryMessagesFromArchive(account);
+ }
+ };
private int accountChangedListenerCount = 0;
private OnRosterUpdate mOnRosterUpdate = null;
private int rosterChangedListenerCount = 0;
@@ -583,8 +591,8 @@ public class XmppConnectionService extends Service {
connection.setOnUnregisteredIqPacketReceivedListener(this.mIqParser);
connection.setOnJinglePacketReceivedListener(this.jingleListener);
connection.setOnBindListener(this.mOnBindListener);
- connection
- .setOnMessageAcknowledgeListener(this.mOnMessageAcknowledgedListener);
+ connection.setOnMessageAcknowledgeListener(this.mOnMessageAcknowledgedListener);
+ connection.setOnAdvancedStreamFeaturesAvailableListener(this.onAdvancedStreamFeaturesAvailable);
return connection;
}
@@ -1231,6 +1239,19 @@ public class XmppConnectionService extends Service {
}
}
+ private void queryMessagesFromArchive(final Account account) {
+ if (account.getXmppConnection() != null && account.getXmppConnection().getFeatures().mam()) {
+ List<Conversation> conversations = getConversations();
+ for (Conversation conversation : conversations) {
+ if (conversation.getMode() == Conversation.MODE_SINGLE && conversation.getAccount() == account) {
+ this.mMessageArchiveService.query(conversation);
+ }
+ }
+ } else {
+ Log.d(Config.LOGTAG,"no mam available");
+ }
+ }
+
public void joinMuc(Conversation conversation) {
Account account = conversation.getAccount();
account.pendingConferenceJoins.remove(conversation);
@@ -1255,7 +1276,6 @@ public class XmppConnectionService extends Service {
packet.addChild("status").setContent("online");
packet.addChild("x", "jabber:x:signed").setContent(sig);
}
- Log.d(Config.LOGTAG,packet.toString());
sendPresencePacket(account, packet);
if (!joinJid.equals(conversation.getContactJid())) {
conversation.setContactJid(joinJid);
@@ -2033,6 +2053,10 @@ public class XmppConnectionService extends Service {
return this.mJingleConnectionManager;
}
+ public MessageArchiveService getMessageArchiveService() {
+ return this.mMessageArchiveService;
+ }
+
public List<Contact> findContacts(Jid jid) {
ArrayList<Contact> contacts = new ArrayList<>();
for (Account account : getAccounts()) {
diff --git a/src/main/java/eu/siacs/conversations/xml/Element.java b/src/main/java/eu/siacs/conversations/xml/Element.java
index 02c3e695..c25b9017 100644
--- a/src/main/java/eu/siacs/conversations/xml/Element.java
+++ b/src/main/java/eu/siacs/conversations/xml/Element.java
@@ -159,4 +159,9 @@ public class Element {
public void setAttribute(String name, int value) {
this.setAttribute(name, Integer.toString(value));
}
+
+ public boolean getAttributeAsBoolean(String name) {
+ String attr = getAttribute(name);
+ return (attr != null && (attr.equalsIgnoreCase("true") || attr.equalsIgnoreCase("1")));
+ }
}
diff --git a/src/main/java/eu/siacs/conversations/xmpp/OnAdvancedStreamFeaturesAvailable.java b/src/main/java/eu/siacs/conversations/xmpp/OnAdvancedStreamFeaturesAvailable.java
new file mode 100644
index 00000000..a41bce86
--- /dev/null
+++ b/src/main/java/eu/siacs/conversations/xmpp/OnAdvancedStreamFeaturesAvailable.java
@@ -0,0 +1,7 @@
+package eu.siacs.conversations.xmpp;
+
+import eu.siacs.conversations.entities.Account;
+
+public interface OnAdvancedStreamFeaturesAvailable {
+ public void onAdvancedStreamFeaturesAvailable(final Account account);
+}
diff --git a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java
index adb96fa2..0e5d26ed 100644
--- a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java
+++ b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java
@@ -107,6 +107,7 @@ public class XmppConnection implements Runnable {
private OnMessagePacketReceived messageListener = null;
private OnStatusChanged statusListener = null;
private OnBindListener bindListener = null;
+ private OnAdvancedStreamFeaturesAvailable advancedStreamFeaturesAvailableListener = null;
private OnMessageAcknowledged acknowledgedListener = null;
private XmppConnectionService mXmppConnectionService = null;
@@ -771,6 +772,9 @@ public class XmppConnection implements Runnable {
if (account.getServer().equals(server.toDomainJid())) {
enableAdvancedStreamFeatures();
+ if (advancedStreamFeaturesAvailableListener != null) {
+ advancedStreamFeaturesAvailableListener.onAdvancedStreamFeaturesAvailable(account);
+ }
}
}
});
@@ -943,6 +947,10 @@ public class XmppConnection implements Runnable {
this.acknowledgedListener = listener;
}
+ public void setOnAdvancedStreamFeaturesAvailableListener(OnAdvancedStreamFeaturesAvailable listener) {
+ this.advancedStreamFeaturesAvailableListener = listener;
+ }
+
public void disconnect(boolean force) {
Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": disconnecting");
try {
diff --git a/src/main/java/eu/siacs/conversations/xmpp/forms/Data.java b/src/main/java/eu/siacs/conversations/xmpp/forms/Data.java
index ff9acb3f..44794c80 100644
--- a/src/main/java/eu/siacs/conversations/xmpp/forms/Data.java
+++ b/src/main/java/eu/siacs/conversations/xmpp/forms/Data.java
@@ -37,6 +37,7 @@ public class Data extends Element {
Field field = getFieldByName(name);
if (field == null) {
field = new Field(name);
+ this.addChild(field);
}
field.setValue(value);
}
@@ -45,6 +46,7 @@ public class Data extends Element {
Field field = getFieldByName(name);
if (field == null) {
field = new Field(name);
+ this.addChild(field);
}
field.setValues(values);
}
@@ -72,4 +74,12 @@ public class Data extends Element {
data.setChildren(element.getChildren());
return data;
}
+
+ public void setFormType(String formType) {
+ this.put("FORM_TYPE",formType);
+ }
+
+ public String getFormType() {
+ return this.getAttribute("FORM_TYPE");
+ }
}