1package eu.siacs.conversations.generator;
2
3import android.util.Base64;
4import eu.siacs.conversations.BuildConfig;
5import eu.siacs.conversations.Config;
6import eu.siacs.conversations.R;
7import eu.siacs.conversations.crypto.axolotl.AxolotlService;
8import eu.siacs.conversations.entities.Account;
9import eu.siacs.conversations.services.XmppConnectionService;
10import eu.siacs.conversations.xml.Namespace;
11import eu.siacs.conversations.xmpp.XmppConnection;
12import java.security.MessageDigest;
13import java.security.NoSuchAlgorithmException;
14import java.text.SimpleDateFormat;
15import java.util.ArrayList;
16import java.util.Arrays;
17import java.util.Collections;
18import java.util.List;
19import java.util.Locale;
20import java.util.TimeZone;
21
22public abstract class AbstractGenerator {
23 private static final SimpleDateFormat DATE_FORMAT =
24 new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.US);
25 private final String[] STATIC_FEATURES = {
26 Namespace.JINGLE,
27 Namespace.JINGLE_APPS_FILE_TRANSFER,
28 Namespace.JINGLE_TRANSPORTS_S5B,
29 Namespace.JINGLE_TRANSPORTS_IBB,
30 Namespace.JINGLE_ENCRYPTED_TRANSPORT,
31 Namespace.JINGLE_ENCRYPTED_TRANSPORT_OMEMO,
32 "http://jabber.org/protocol/muc",
33 "jabber:x:conference",
34 Namespace.OOB,
35 "http://jabber.org/protocol/caps",
36 "http://jabber.org/protocol/disco#info",
37 "urn:xmpp:avatar:metadata+notify",
38 Namespace.NICK + "+notify",
39 "urn:xmpp:ping",
40 "jabber:iq:version",
41 "http://jabber.org/protocol/chatstates",
42 Namespace.REACTIONS
43 };
44 private final String[] MESSAGE_CONFIRMATION_FEATURES = {
45 "urn:xmpp:chat-markers:0", "urn:xmpp:receipts"
46 };
47 private final String[] MESSAGE_CORRECTION_FEATURES = {"urn:xmpp:message-correct:0"};
48 private final String[] PRIVACY_SENSITIVE = {
49 "urn:xmpp:time" // XEP-0202: Entity Time leaks time zone
50 };
51 private final String[] VOIP_NAMESPACES = {
52 Namespace.JINGLE_TRANSPORT_ICE_UDP,
53 Namespace.JINGLE_FEATURE_AUDIO,
54 Namespace.JINGLE_FEATURE_VIDEO,
55 Namespace.JINGLE_APPS_RTP,
56 Namespace.JINGLE_APPS_DTLS,
57 Namespace.JINGLE_MESSAGE
58 };
59 protected XmppConnectionService mXmppConnectionService;
60
61 AbstractGenerator(XmppConnectionService service) {
62 this.mXmppConnectionService = service;
63 }
64
65 public static String getTimestamp(long time) {
66 DATE_FORMAT.setTimeZone(TimeZone.getTimeZone("UTC"));
67 return DATE_FORMAT.format(time);
68 }
69
70 String getIdentityVersion() {
71 return BuildConfig.VERSION_NAME;
72 }
73
74 String getIdentityName() {
75 return BuildConfig.APP_NAME;
76 }
77
78 String getIdentityType() {
79 if ("chromium".equals(android.os.Build.BRAND)) {
80 return "pc";
81 } else {
82 return mXmppConnectionService.getString(R.string.default_resource).toLowerCase();
83 }
84 }
85
86 String getCapHash(final Account account) {
87 StringBuilder s = new StringBuilder();
88 s.append("client/")
89 .append(getIdentityType())
90 .append("//")
91 .append(getIdentityName())
92 .append('<');
93 MessageDigest md;
94 try {
95 md = MessageDigest.getInstance("SHA-1");
96 } catch (NoSuchAlgorithmException e) {
97 return null;
98 }
99
100 for (String feature : getFeatures(account)) {
101 s.append(feature).append('<');
102 }
103 final byte[] sha1 = md.digest(s.toString().getBytes());
104 return Base64.encodeToString(sha1, Base64.NO_WRAP);
105 }
106
107 public List<String> getFeatures(final Account account) {
108 final XmppConnection connection = account.getXmppConnection();
109 final ArrayList<String> features = new ArrayList<>(Arrays.asList(STATIC_FEATURES));
110 features.add("http://jabber.org/protocol/xhtml-im");
111 features.add("urn:xmpp:bob");
112 if (Config.MESSAGE_DISPLAYED_SYNCHRONIZATION) {
113 features.add(Namespace.MDS_DISPLAYED + "+notify");
114 }
115 if (mXmppConnectionService.confirmMessages()) {
116 features.addAll(Arrays.asList(MESSAGE_CONFIRMATION_FEATURES));
117 }
118 if (mXmppConnectionService.allowMessageCorrection()) {
119 features.addAll(Arrays.asList(MESSAGE_CORRECTION_FEATURES));
120 }
121 if (Config.supportOmemo()) {
122 features.add(AxolotlService.PEP_DEVICE_LIST_NOTIFY);
123 }
124 if (!mXmppConnectionService.useTorToConnect() && !account.isOnion()) {
125 features.addAll(Arrays.asList(PRIVACY_SENSITIVE));
126 features.addAll(Arrays.asList(VOIP_NAMESPACES));
127 features.add(Namespace.JINGLE_TRANSPORT_WEBRTC_DATA_CHANNEL);
128 }
129 if (mXmppConnectionService.broadcastLastActivity()) {
130 features.add(Namespace.IDLE);
131 }
132 if (connection != null && connection.getFeatures().bookmarks2()) {
133 features.add(Namespace.BOOKMARKS2 + "+notify");
134 } else {
135 features.add(Namespace.BOOKMARKS + "+notify");
136 }
137
138 Collections.sort(features);
139 return features;
140 }
141}