MessageArchiveService.java

  1package eu.siacs.conversations.services;
  2
  3import android.util.Log;
  4
  5import java.math.BigInteger;
  6import java.util.HashSet;
  7import java.util.List;
  8
  9import eu.siacs.conversations.Config;
 10import eu.siacs.conversations.entities.Account;
 11import eu.siacs.conversations.entities.Conversation;
 12import eu.siacs.conversations.xml.Element;
 13import eu.siacs.conversations.xmpp.OnAdvancedStreamFeaturesLoaded;
 14import eu.siacs.conversations.xmpp.OnIqPacketReceived;
 15import eu.siacs.conversations.xmpp.jid.Jid;
 16import eu.siacs.conversations.xmpp.stanzas.IqPacket;
 17
 18public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded {
 19
 20	private final XmppConnectionService mXmppConnectionService;
 21
 22	private final HashSet<Query> queries = new HashSet<Query>();
 23
 24	public MessageArchiveService(final XmppConnectionService service) {
 25		this.mXmppConnectionService = service;
 26	}
 27
 28	public void query(final Conversation conversation) {
 29		synchronized (this.queries) {
 30			final Account account = conversation.getAccount();
 31			long start = conversation.getLastMessageTransmitted();
 32			long end = account.getXmppConnection().getLastSessionEstablished();
 33			if (end - start >= Config.MAX_HISTORY_AGE) {
 34				start = end - Config.MAX_HISTORY_AGE;
 35			}
 36			final Query query = new Query(conversation, start, end);
 37			this.queries.add(query);
 38			IqPacket packet = this.mXmppConnectionService.getIqGenerator().queryMessageArchiveManagement(query);
 39			this.mXmppConnectionService.sendIqPacket(account, packet, new OnIqPacketReceived() {
 40				@Override
 41				public void onIqPacketReceived(Account account, IqPacket packet) {
 42					if (packet.getType() == IqPacket.TYPE_ERROR) {
 43						finalizeQuery(query);
 44					}
 45				}
 46			});
 47		}
 48	}
 49
 50	private void finalizeQuery(Query query) {
 51		synchronized (this.queries) {
 52			this.queries.remove(query);
 53		}
 54		final Conversation conversation = query.getConversation();
 55		conversation.sort();
 56		if (conversation.setLastMessageTransmitted(query.getEnd())) {
 57			this.mXmppConnectionService.databaseBackend.updateConversation(conversation);
 58		}
 59		this.mXmppConnectionService.updateConversationUi();
 60	}
 61
 62	public void processFin(Element fin) {
 63		if (fin == null) {
 64			return;
 65		}
 66		Query query = findQuery(fin.getAttribute("queryid"));
 67		if (query == null) {
 68			return;
 69		}
 70		boolean complete = fin.getAttributeAsBoolean("complete");
 71		Element set = fin.findChild("set","http://jabber.org/protocol/rsm");
 72		Element last = set == null ? null : set.findChild("last");
 73		if (complete || last == null) {
 74			final Account account = query.getConversation().getAccount();
 75			Log.d(Config.LOGTAG,account.getJid().toBareJid().toString()+": completed mam query for "+query.getWith().toString());
 76			this.finalizeQuery(query);
 77		} else {
 78			final Query nextQuery = query.next(last == null ? null : last.getContent());
 79			IqPacket packet = this.mXmppConnectionService.getIqGenerator().queryMessageArchiveManagement(nextQuery);
 80			synchronized (this.queries) {
 81				this.queries.remove(query);
 82				this.queries.add(nextQuery);
 83			}
 84			this.mXmppConnectionService.sendIqPacket(query.getConversation().getAccount(),packet,new OnIqPacketReceived() {
 85				@Override
 86				public void onIqPacketReceived(Account account, IqPacket packet) {
 87					if (packet.getType() == IqPacket.TYPE_ERROR) {
 88						finalizeQuery(nextQuery);
 89					}
 90				}
 91			});
 92		}
 93	}
 94
 95	private Query findQuery(String id) {
 96		if (id == null) {
 97			return null;
 98		}
 99		synchronized (this.queries) {
100			for(Query query : this.queries) {
101				if (query.getQueryId().equals(id)) {
102					return query;
103				}
104			}
105			return null;
106		}
107	}
108
109	@Override
110	public void onAdvancedStreamFeaturesAvailable(Account account) {
111		if (account.getXmppConnection() != null && account.getXmppConnection().getFeatures().mam()) {
112			List<Conversation> conversations = mXmppConnectionService.getConversations();
113			for (Conversation conversation : conversations) {
114				if (conversation.getMode() == Conversation.MODE_SINGLE && conversation.getAccount() == account) {
115					this.query(conversation);
116				}
117			}
118		} else {
119			Log.d(Config.LOGTAG,"no mam available");
120		}
121	}
122
123	public class Query {
124		private long start;
125		private long end;
126		private Jid with;
127		private String queryId;
128		private String after = null;
129		private Conversation conversation;
130
131		public Query(Conversation conversation, long start, long end) {
132			this.conversation = conversation;
133			this.with = conversation.getContactJid().toBareJid();
134			this.start = start;
135			this.end = end;
136			this.queryId = new BigInteger(50, mXmppConnectionService.getRNG()).toString(32);
137		}
138
139		public Query next(String after) {
140			Query query = new Query(this.conversation,this.start,this.end);
141			query.after = after;
142			return query;
143		}
144
145		public String getAfter() {
146			return after;
147		}
148
149		public String getQueryId() {
150			return queryId;
151		}
152
153		public Jid getWith() {
154			return with;
155		}
156
157		public long getStart() {
158			return start;
159		}
160
161		public long getEnd() {
162			return end;
163		}
164
165		public Conversation getConversation() {
166			return conversation;
167		}
168	}
169}