AbstractParser.java

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