AbstractParser.java

  1package eu.siacs.conversations.parser;
  2
  3
  4import java.text.ParseException;
  5import java.text.SimpleDateFormat;
  6import java.util.ArrayList;
  7import java.util.Date;
  8import java.util.List;
  9import java.util.Locale;
 10import java.util.Set;
 11import java.util.TreeSet;
 12
 13import eu.siacs.conversations.entities.Account;
 14import eu.siacs.conversations.entities.Contact;
 15import eu.siacs.conversations.entities.Conversation;
 16import eu.siacs.conversations.entities.MucOptions;
 17import eu.siacs.conversations.services.XmppConnectionService;
 18import eu.siacs.conversations.xml.Element;
 19import eu.siacs.conversations.xmpp.InvalidJid;
 20import eu.siacs.conversations.xmpp.Jid;
 21import im.conversations.android.xmpp.model.stanza.Stanza;
 22
 23public abstract class AbstractParser {
 24
 25	protected final XmppConnectionService mXmppConnectionService;
 26	protected final Account account;
 27
 28	protected AbstractParser(final XmppConnectionService service, final Account account) {
 29		this.mXmppConnectionService = service;
 30		this.account = account;
 31	}
 32
 33	public static Long parseTimestamp(Element element, Long d) {
 34		return parseTimestamp(element,d,false);
 35	}
 36
 37	public static Long parseTimestamp(Element element, Long d, boolean ignoreCsiAndSm) {
 38		long min = Long.MAX_VALUE;
 39		boolean returnDefault = true;
 40		final Jid to;
 41		if (ignoreCsiAndSm && element instanceof Stanza stanza) {
 42			to = stanza.getTo();
 43		} else {
 44			to = null;
 45		}
 46		for(Element child : element.getChildren()) {
 47			if ("delay".equals(child.getName()) && "urn:xmpp:delay".equals(child.getNamespace())) {
 48				final Jid f = to == null ? null : InvalidJid.getNullForInvalid(child.getAttributeAsJid("from"));
 49				if (f != null && (to.asBareJid().equals(f) || to.getDomain().equals(f))) {
 50					continue;
 51				}
 52				final String stamp = child.getAttribute("stamp");
 53				if (stamp != null) {
 54					try {
 55						min = Math.min(min,AbstractParser.parseTimestamp(stamp));
 56						returnDefault = false;
 57					} catch (Throwable t) {
 58						//ignore
 59					}
 60				}
 61			}
 62		}
 63		if (returnDefault) {
 64			return d;
 65		} else {
 66			return min;
 67		}
 68	}
 69
 70	public static long parseTimestamp(Element element) {
 71		return parseTimestamp(element, System.currentTimeMillis());
 72	}
 73
 74	public static long parseTimestamp(String timestamp) throws ParseException {
 75		timestamp = timestamp.replace("Z", "+0000");
 76		SimpleDateFormat dateFormat;
 77		long ms;
 78		if (timestamp.length() >= 25 && timestamp.charAt(19) == '.') {
 79			String millis = timestamp.substring(19,timestamp.length() - 5);
 80			try {
 81				double fractions = Double.parseDouble("0" + millis);
 82				ms = Math.round(1000 * fractions);
 83			} catch (NumberFormatException e) {
 84				ms = 0;
 85			}
 86		} else {
 87			ms = 0;
 88		}
 89		timestamp = timestamp.substring(0,19)+timestamp.substring(timestamp.length() -5);
 90		dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ",Locale.US);
 91		return Math.min(dateFormat.parse(timestamp).getTime()+ms, System.currentTimeMillis());
 92	}
 93
 94    public static long getTimestamp(final String input) throws ParseException {
 95        if (input == null) {
 96            throw new IllegalArgumentException("timestamp should not be null");
 97        }
 98        final String timestamp = input.replace("Z", "+0000");
 99        final SimpleDateFormat simpleDateFormat =
100                new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.US);
101        final long milliseconds = getMilliseconds(timestamp);
102        final String formatted =
103                timestamp.substring(0, 19) + timestamp.substring(timestamp.length() - 5);
104        final Date date = simpleDateFormat.parse(formatted);
105        if (date == null) {
106            throw new IllegalArgumentException("Date was null");
107        }
108        return date.getTime() + milliseconds;
109    }
110
111    private static long getMilliseconds(final String timestamp) {
112        if (timestamp.length() >= 25 && timestamp.charAt(19) == '.') {
113            final String millis = timestamp.substring(19, timestamp.length() - 5);
114            try {
115                double fractions = Double.parseDouble("0" + millis);
116                return Math.round(1000 * fractions);
117            } catch (NumberFormatException e) {
118                return 0;
119            }
120        } else {
121            return 0;
122        }
123    }
124
125	protected void updateLastseen(final Account account, final Jid from) {
126		final Contact contact = account.getRoster().getContact(from);
127		contact.setLastResource(from.isBareJid() ? "" : from.getResource());
128	}
129
130	protected static String avatarData(Element items) {
131		Element item = items.findChild("item");
132		if (item == null) {
133			return null;
134		}
135		return item.findChildContent("data", "urn:xmpp:avatar:data");
136	}
137
138	public static MucOptions.User parseItem(Conversation conference, Element item) {
139		return parseItem(conference,item,null,null,null,new Element("hats", "urn:xmpp:hats:0"));
140	}
141
142	public static MucOptions.User parseItem(Conversation conference, Element item, Jid fullJid, final Element occupantId, final String nicknameIn, final Element hatsEl) {
143		final String local = conference.getJid().getLocal();
144		final String domain = conference.getJid().getDomain().toEscapedString();
145		String affiliation = item.getAttribute("affiliation");
146		String role = item.getAttribute("role");
147		String nick = item.getAttribute("nick");
148		if (nick != null && fullJid == null) {
149			try {
150				fullJid = Jid.of(local, domain, nick);
151			} catch (IllegalArgumentException e) {
152				fullJid = null;
153			}
154		}
155		Jid realJid = item.getAttributeAsJid("jid");
156		if (fullJid != null) nick = fullJid.getResource();
157		String nickname = null;
158		if (nick != null && nicknameIn != null) nickname = nick.equals(nicknameIn) ? nick : null;
159		try {
160			if (nickname == null && nicknameIn != null && nick != null && gnu.inet.encoding.Punycode.decode(nick).equals(nicknameIn)) {
161				nickname = nicknameIn;
162			}
163		} catch (final Exception e) { }
164		Set<MucOptions.Hat> hats = new TreeSet<>();
165		for (Element hat : hatsEl.getChildren()) {
166			if ("hat".equals(hat.getName()) && ("urn:xmpp:hats:0".equals(hat.getNamespace()) || "xmpp:prosody.im/protocol/hats:1".equals(hat.getNamespace()))) {
167				hats.add(new MucOptions.Hat(hat));
168			}
169		}
170		MucOptions.User user = new MucOptions.User(conference.getMucOptions(), fullJid, occupantId == null ? null : occupantId.getAttribute("id"), nickname, hatsEl == null ? null : hats);
171		if (InvalidJid.isValid(realJid)) {
172			user.setRealJid(realJid);
173		}
174		user.setAffiliation(affiliation);
175		user.setRole(role);
176		return user;
177	}
178
179	public static String extractErrorMessage(final Element packet) {
180		final Element error = packet.findChild("error");
181		if (error != null && error.getChildren().size() > 0) {
182			final List<String> errorNames = orderedElementNames(error.getChildren());
183			final String text = error.findChildContent("text");
184			if (text != null && !text.trim().isEmpty()) {
185				return prefixError(errorNames)+text;
186			} else if (errorNames.size() > 0){
187				return prefixError(errorNames)+errorNames.get(0).replace("-"," ");
188			}
189		}
190		return null;
191	}
192
193	public static String errorMessage(Element packet) {
194		final Element error = packet.findChild("error");
195		if (error != null && error.getChildren().size() > 0) {
196			final List<String> errorNames = orderedElementNames(error.getChildren());
197			final String text = error.findChildContent("text");
198			if (text != null && !text.trim().isEmpty()) {
199				return text;
200			} else if (errorNames.size() > 0){
201				return errorNames.get(0).replace("-"," ");
202			}
203		}
204		return null;
205	}
206
207	private static String prefixError(List<String> errorNames) {
208		if (errorNames.size() > 0) {
209			return errorNames.get(0)+'\u001f';
210		}
211		return "";
212	}
213
214	private static List<String> orderedElementNames(List<Element> children) {
215		List<String> names = new ArrayList<>();
216		for(Element child : children) {
217			final String name = child.getName();
218			if (name != null && !name.equals("text")) {
219				if ("urn:ietf:params:xml:ns:xmpp-stanzas".equals(child.getNamespace())) {
220					names.add(name);
221				} else {
222					names.add(0, name);
223				}
224			}
225		}
226		return names;
227	}
228}