very basic mam support
This commit is contained in:
parent
34558cc277
commit
4a94389f05
|
@ -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() {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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()) {
|
||||
|
|
|
@ -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")));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
package eu.siacs.conversations.xmpp;
|
||||
|
||||
import eu.siacs.conversations.entities.Account;
|
||||
|
||||
public interface OnAdvancedStreamFeaturesAvailable {
|
||||
public void onAdvancedStreamFeaturesAvailable(final Account account);
|
||||
}
|
|
@ -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 {
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue