1package eu.siacs.conversations.parser;
2
3import android.util.Log;
4
5import java.util.ArrayList;
6import java.util.Collection;
7
8import eu.siacs.conversations.Config;
9import eu.siacs.conversations.entities.Account;
10import eu.siacs.conversations.entities.Contact;
11import eu.siacs.conversations.services.XmppConnectionService;
12import eu.siacs.conversations.utils.Xmlns;
13import eu.siacs.conversations.xml.Element;
14import eu.siacs.conversations.xmpp.OnIqPacketReceived;
15import eu.siacs.conversations.xmpp.OnUpdateBlocklist;
16import eu.siacs.conversations.xmpp.jid.Jid;
17import eu.siacs.conversations.xmpp.stanzas.IqPacket;
18
19public class IqParser extends AbstractParser implements OnIqPacketReceived {
20
21 public IqParser(final XmppConnectionService service) {
22 super(service);
23 }
24
25 public void rosterItems(final Account account, final Element query) {
26 final String version = query.getAttribute("ver");
27 if (version != null) {
28 account.getRoster().setVersion(version);
29 }
30 for (final Element item : query.getChildren()) {
31 if (item.getName().equals("item")) {
32 final Jid jid = item.getAttributeAsJid("jid");
33 if (jid == null) {
34 continue;
35 }
36 final String name = item.getAttribute("name");
37 final String subscription = item.getAttribute("subscription");
38 final Contact contact = account.getRoster().getContact(jid);
39 if (!contact.getOption(Contact.Options.DIRTY_PUSH)) {
40 contact.setServerName(name);
41 contact.parseGroupsFromElement(item);
42 }
43 if (subscription != null) {
44 if (subscription.equals("remove")) {
45 contact.resetOption(Contact.Options.IN_ROSTER);
46 contact.resetOption(Contact.Options.DIRTY_DELETE);
47 contact.resetOption(Contact.Options.PREEMPTIVE_GRANT);
48 } else {
49 contact.setOption(Contact.Options.IN_ROSTER);
50 contact.resetOption(Contact.Options.DIRTY_PUSH);
51 contact.parseSubscriptionFromElement(item);
52 }
53 }
54 mXmppConnectionService.getAvatarService().clear(contact);
55 }
56 }
57 mXmppConnectionService.updateConversationUi();
58 mXmppConnectionService.updateRosterUi();
59 }
60
61 public String avatarData(final IqPacket packet) {
62 final Element pubsub = packet.findChild("pubsub",
63 "http://jabber.org/protocol/pubsub");
64 if (pubsub == null) {
65 return null;
66 }
67 final Element items = pubsub.findChild("items");
68 if (items == null) {
69 return null;
70 }
71 return super.avatarData(items);
72 }
73
74 @Override
75 public void onIqPacketReceived(final Account account, final IqPacket packet) {
76 if (packet.hasChild("query", "jabber:iq:roster")) {
77 final Jid from = packet.getFrom();
78 if ((from == null) || (from.equals(account.getJid().toBareJid()))) {
79 final Element query = packet.findChild("query");
80 this.rosterItems(account, query);
81 }
82 } else if (packet.hasChild("block", Xmlns.BLOCKING) || packet.hasChild("blocklist", Xmlns.BLOCKING)) {
83 // Only accept block list changes from the server.
84 // The server should probably prevent other people from faking a blocklist push,
85 // but just in case let's prevent it client side as well.
86 final Jid from = packet.getFrom();
87 if (from == null || from.equals(account.getServer()) || from.equals(account.getJid().toBareJid())) {
88 Log.d(Config.LOGTAG, "Received blocklist update from server");
89 final Element blocklist = packet.findChild("blocklist", Xmlns.BLOCKING);
90 final Element block = packet.findChild("block", Xmlns.BLOCKING);
91 final Collection<Element> items = blocklist != null ? blocklist.getChildren() :
92 (block != null ? block.getChildren() : null);
93 // If this is a response to a blocklist query, clear the block list and replace with the new one.
94 // Otherwise, just update the existing blocklist.
95 if (packet.getType() == IqPacket.TYPE_RESULT) {
96 account.clearBlocklist();
97 }
98 if (items != null) {
99 final Collection<Jid> jids = new ArrayList<>(items.size());
100 // Create a collection of Jids from the packet
101 for (final Element item : items) {
102 if (item.getName().equals("item")) {
103 final Jid jid = item.getAttributeAsJid("jid");
104 if (jid != null) {
105 jids.add(jid);
106 }
107 }
108 }
109 account.getBlocklist().addAll(jids);
110 }
111 // Update the UI
112 mXmppConnectionService.updateBlocklistUi(OnUpdateBlocklist.Status.BLOCKED);
113 } else {
114 Log.d(Config.LOGTAG, "Received blocklist update from invalid jid: " + from.toString());
115 }
116 } else if (packet.hasChild("unblock", Xmlns.BLOCKING)) {
117 final Jid from = packet.getFrom();
118 if ((from == null || from.equals(account.getServer()) || from.equals(account.getJid().toBareJid())) &&
119 packet.getType() == IqPacket.TYPE_SET) {
120 Log.d(Config.LOGTAG, "Received unblock update from server");
121 final Collection<Element> items = packet.getChildren().get(0).getChildren();
122 if (items.size() == 0) {
123 // No children to unblock == unblock all
124 account.getBlocklist().clear();
125 } else {
126 final Collection<Jid> jids = new ArrayList<>(items.size());
127 for (final Element item : items) {
128 if (item.getName().equals("item")) {
129 final Jid jid = item.getAttributeAsJid("jid");
130 if (jid != null) {
131 jids.add(jid);
132 }
133 }
134 }
135 account.getBlocklist().removeAll(jids);
136 mXmppConnectionService.updateBlocklistUi(OnUpdateBlocklist.Status.UNBLOCKED);
137 }
138 } else {
139 if (packet.getType() == IqPacket.TYPE_SET) {
140 Log.d(Config.LOGTAG, "Received unblock update from invalid jid " + from.toString());
141 } else {
142 Log.d(Config.LOGTAG, "Received unblock update with invalid type " + packet.getType());
143 }
144 }
145 } else {
146 if (packet.getFrom() == null) {
147 Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": received iq with invalid from "+packet.toString());
148 return;
149 } else if (packet.hasChild("open", "http://jabber.org/protocol/ibb")
150 || packet.hasChild("data", "http://jabber.org/protocol/ibb")) {
151 mXmppConnectionService.getJingleConnectionManager()
152 .deliverIbbPacket(account, packet);
153 } else if (packet.hasChild("query", "http://jabber.org/protocol/disco#info")) {
154 final IqPacket response = mXmppConnectionService.getIqGenerator()
155 .discoResponse(packet);
156 account.getXmppConnection().sendIqPacket(response, null);
157 } else if (packet.hasChild("ping", "urn:xmpp:ping")) {
158 final IqPacket response = packet.generateRespone(IqPacket.TYPE_RESULT);
159 mXmppConnectionService.sendIqPacket(account, response, null);
160 } else {
161 if ((packet.getType() == IqPacket.TYPE_GET)
162 || (packet.getType() == IqPacket.TYPE_SET)) {
163 final IqPacket response = packet.generateRespone(IqPacket.TYPE_ERROR);
164 final Element error = response.addChild("error");
165 error.setAttribute("type", "cancel");
166 error.addChild("feature-not-implemented",
167 "urn:ietf:params:xml:ns:xmpp-stanzas");
168 account.getXmppConnection().sendIqPacket(response, null);
169 }
170 }
171 }
172 }
173
174}