Browse Source

Use Gradle build system

lolipop_dns_server_discovery
Sam Whited 9 years ago
commit
64bdd7e731
  1. 41
      .gitignore
  2. 9
      .gitmodules
  3. 16
      build.gradle
  4. 28
      conversations/build.gradle
  5. 124
      conversations/src/main/AndroidManifest.xml
  6. 25
      conversations/src/main/java/eu/siacs/conversations/Config.java
  7. 231
      conversations/src/main/java/eu/siacs/conversations/crypto/OtrEngine.java
  8. 385
      conversations/src/main/java/eu/siacs/conversations/crypto/PgpEngine.java
  9. 21
      conversations/src/main/java/eu/siacs/conversations/entities/AbstractEntity.java
  10. 399
      conversations/src/main/java/eu/siacs/conversations/entities/Account.java
  11. 137
      conversations/src/main/java/eu/siacs/conversations/entities/Bookmark.java
  12. 367
      conversations/src/main/java/eu/siacs/conversations/entities/Contact.java
  13. 500
      conversations/src/main/java/eu/siacs/conversations/entities/Conversation.java
  14. 21
      conversations/src/main/java/eu/siacs/conversations/entities/Downloadable.java
  15. 154
      conversations/src/main/java/eu/siacs/conversations/entities/DownloadableFile.java
  16. 7
      conversations/src/main/java/eu/siacs/conversations/entities/ListItem.java
  17. 478
      conversations/src/main/java/eu/siacs/conversations/entities/Message.java
  18. 369
      conversations/src/main/java/eu/siacs/conversations/entities/MucOptions.java
  19. 76
      conversations/src/main/java/eu/siacs/conversations/entities/Presences.java
  20. 83
      conversations/src/main/java/eu/siacs/conversations/entities/Roster.java
  21. 48
      conversations/src/main/java/eu/siacs/conversations/generator/AbstractGenerator.java
  22. 96
      conversations/src/main/java/eu/siacs/conversations/generator/IqGenerator.java
  23. 178
      conversations/src/main/java/eu/siacs/conversations/generator/MessageGenerator.java
  24. 57
      conversations/src/main/java/eu/siacs/conversations/generator/PresenceGenerator.java
  25. 255
      conversations/src/main/java/eu/siacs/conversations/http/HttpConnection.java
  26. 28
      conversations/src/main/java/eu/siacs/conversations/http/HttpConnectionManager.java
  27. 92
      conversations/src/main/java/eu/siacs/conversations/parser/AbstractParser.java
  28. 92
      conversations/src/main/java/eu/siacs/conversations/parser/IqParser.java
  29. 517
      conversations/src/main/java/eu/siacs/conversations/parser/MessageParser.java
  30. 133
      conversations/src/main/java/eu/siacs/conversations/parser/PresenceParser.java
  31. 335
      conversations/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java
  32. 480
      conversations/src/main/java/eu/siacs/conversations/persistance/FileBackend.java
  33. 5
      conversations/src/main/java/eu/siacs/conversations/persistance/OnPhoneContactsMerged.java
  34. 23
      conversations/src/main/java/eu/siacs/conversations/services/AbstractConnectionManager.java
  35. 298
      conversations/src/main/java/eu/siacs/conversations/services/AvatarService.java
  36. 24
      conversations/src/main/java/eu/siacs/conversations/services/EventReceiver.java
  37. 237
      conversations/src/main/java/eu/siacs/conversations/services/NotificationService.java
  38. 1927
      conversations/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java
  39. 145
      conversations/src/main/java/eu/siacs/conversations/ui/ChooseContactActivity.java
  40. 280
      conversations/src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java
  41. 436
      conversations/src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java
  42. 947
      conversations/src/main/java/eu/siacs/conversations/ui/ConversationActivity.java
  43. 781
      conversations/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java
  44. 423
      conversations/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java
  45. 39
      conversations/src/main/java/eu/siacs/conversations/ui/EditMessage.java
  46. 217
      conversations/src/main/java/eu/siacs/conversations/ui/ManageAccountActivity.java
  47. 242
      conversations/src/main/java/eu/siacs/conversations/ui/PublishProfilePictureActivity.java
  48. 74
      conversations/src/main/java/eu/siacs/conversations/ui/SettingsActivity.java
  49. 15
      conversations/src/main/java/eu/siacs/conversations/ui/SettingsFragment.java
  50. 185
      conversations/src/main/java/eu/siacs/conversations/ui/ShareWithActivity.java
  51. 677
      conversations/src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java
  52. 11
      conversations/src/main/java/eu/siacs/conversations/ui/UiCallback.java
  53. 637
      conversations/src/main/java/eu/siacs/conversations/ui/XmppActivity.java
  54. 102
      conversations/src/main/java/eu/siacs/conversations/ui/adapter/AccountAdapter.java
  55. 135
      conversations/src/main/java/eu/siacs/conversations/ui/adapter/ConversationAdapter.java
  56. 74
      conversations/src/main/java/eu/siacs/conversations/ui/adapter/KnownHostsAdapter.java
  57. 44
      conversations/src/main/java/eu/siacs/conversations/ui/adapter/ListItemAdapter.java
  58. 560
      conversations/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java
  59. 112
      conversations/src/main/java/eu/siacs/conversations/utils/CryptoHelper.java
  60. 185
      conversations/src/main/java/eu/siacs/conversations/utils/DNSHelper.java
  61. 44
      conversations/src/main/java/eu/siacs/conversations/utils/ExceptionHandler.java
  62. 117
      conversations/src/main/java/eu/siacs/conversations/utils/ExceptionHelper.java
  63. 9
      conversations/src/main/java/eu/siacs/conversations/utils/OnPhoneContactsLoadedListener.java
  64. 327
      conversations/src/main/java/eu/siacs/conversations/utils/PRNGFixes.java
  65. 95
      conversations/src/main/java/eu/siacs/conversations/utils/PhoneHelper.java
  66. 225
      conversations/src/main/java/eu/siacs/conversations/utils/UIHelper.java
  67. 14
      conversations/src/main/java/eu/siacs/conversations/utils/Validator.java
  68. 12
      conversations/src/main/java/eu/siacs/conversations/utils/XmlHelper.java
  69. 54
      conversations/src/main/java/eu/siacs/conversations/utils/zlib/ZLibInputStream.java
  70. 95
      conversations/src/main/java/eu/siacs/conversations/utils/zlib/ZLibOutputStream.java
  71. 148
      conversations/src/main/java/eu/siacs/conversations/xml/Element.java
  72. 104
      conversations/src/main/java/eu/siacs/conversations/xml/Tag.java
  73. 114
      conversations/src/main/java/eu/siacs/conversations/xml/TagWriter.java
  74. 141
      conversations/src/main/java/eu/siacs/conversations/xml/XmlReader.java
  75. 7
      conversations/src/main/java/eu/siacs/conversations/xmpp/OnBindListener.java
  76. 7
      conversations/src/main/java/eu/siacs/conversations/xmpp/OnContactStatusChanged.java
  77. 8
      conversations/src/main/java/eu/siacs/conversations/xmpp/OnIqPacketReceived.java
  78. 7
      conversations/src/main/java/eu/siacs/conversations/xmpp/OnMessageAcknowledged.java
  79. 8
      conversations/src/main/java/eu/siacs/conversations/xmpp/OnMessagePacketReceived.java
  80. 8
      conversations/src/main/java/eu/siacs/conversations/xmpp/OnPresencePacketReceived.java
  81. 7
      conversations/src/main/java/eu/siacs/conversations/xmpp/OnStatusChanged.java
  82. 5
      conversations/src/main/java/eu/siacs/conversations/xmpp/PacketReceived.java
  83. 1130
      conversations/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java
  84. 143
      conversations/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleCandidate.java
  85. 910
      conversations/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java
  86. 163
      conversations/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java
  87. 191
      conversations/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleInbandTransport.java
  88. 212
      conversations/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java
  89. 13
      conversations/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleTransport.java
  90. 9
      conversations/src/main/java/eu/siacs/conversations/xmpp/jingle/OnFileTransmissionStatusChanged.java
  91. 9
      conversations/src/main/java/eu/siacs/conversations/xmpp/jingle/OnJinglePacketReceived.java
  92. 6
      conversations/src/main/java/eu/siacs/conversations/xmpp/jingle/OnPrimaryCandidateFound.java
  93. 7
      conversations/src/main/java/eu/siacs/conversations/xmpp/jingle/OnTransportConnected.java
  94. 102
      conversations/src/main/java/eu/siacs/conversations/xmpp/jingle/stanzas/Content.java
  95. 95
      conversations/src/main/java/eu/siacs/conversations/xmpp/jingle/stanzas/JinglePacket.java
  96. 13
      conversations/src/main/java/eu/siacs/conversations/xmpp/jingle/stanzas/Reason.java
  97. 71
      conversations/src/main/java/eu/siacs/conversations/xmpp/pep/Avatar.java
  98. 34
      conversations/src/main/java/eu/siacs/conversations/xmpp/stanzas/AbstractStanza.java
  99. 76
      conversations/src/main/java/eu/siacs/conversations/xmpp/stanzas/IqPacket.java
  100. 66
      conversations/src/main/java/eu/siacs/conversations/xmpp/stanzas/MessagePacket.java
  101. Some files were not shown because too many files have changed in this diff Show More

41
.gitignore vendored

@ -0,0 +1,41 @@ @@ -0,0 +1,41 @@
.classpath
*.swp
.settings
# https://github.com/github/gitignore/blob/master/Gradle.gitignore
.gradle/
gradle/
build/
# Ignore Gradle GUI config
gradle-app.setting
# https://github.com/github/gitignore/blob/master/Android.gitignore
# Built application files
*.apk
*.ap_
# Files for the Dalvik VM
*.dex
# Java class files
*.class
# Generated files
bin/
gen/
# Local configuration file (sdk path, etc)
local.properties
# Proguard folder generated by Eclipse
proguard/
# Log Files
*.log
*.iml
.idea
import-summary.txt
*.jar

9
.gitmodules vendored

@ -0,0 +1,9 @@ @@ -0,0 +1,9 @@
[submodule "minidns"]
path = minidns
url = https://github.com/rtreffer/minidns.git
[submodule "openpgpapilib"]
path = openpgpapilib
url = https://github.com/open-keychain/openpgp-api-lib.git
[submodule "memorizingTrustManager"]
path = memorizingTrustManager
url = https://github.com/iNPUTmice/MemorizingTrustManager.git

16
build.gradle

@ -0,0 +1,16 @@ @@ -0,0 +1,16 @@
// Top-level build file where you can add configuration options common to all
// sub-projects/modules.
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:0.12.2'
}
}
allprojects {
repositories {
jcenter()
}
}

28
conversations/build.gradle

@ -0,0 +1,28 @@ @@ -0,0 +1,28 @@
apply plugin: 'com.android.application'
android {
compileSdkVersion 19
buildToolsVersion "20.0.0"
defaultConfig {
applicationId "eu.siacs.conversations"
minSdkVersion 14
targetSdkVersion 19
}
buildTypes {
release {
runProguard false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
}
}
}
dependencies {
compile project(':minidns')
compile project(':openpgpapilib')
compile project(':memorizingTrustManager')
compile files('libs/android-support-v13.jar')
compile files('libs/bcprov-jdk15on-150.jar')
compile files('libs/otr4j-0.10.jar')
}

124
conversations/src/main/AndroidManifest.xml

@ -0,0 +1,124 @@ @@ -0,0 +1,124 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="eu.siacs.conversations"
android:versionCode="32"
android:versionName="0.8-alpha" >
<uses-sdk
android:minSdkVersion="14"
android:targetSdkVersion="19" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.READ_PROFILE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.VIBRATE" />
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
tools:replace="android:label"
android:theme="@style/ConversationsTheme" >
<service android:name="eu.siacs.conversations.services.XmppConnectionService" />
<receiver android:name="eu.siacs.conversations.services.EventReceiver" >
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
<action android:name="android.intent.action.ACTION_SHUTDOWN" />
</intent-filter>
</receiver>
<activity
android:name="eu.siacs.conversations.ui.ConversationActivity"
android:label="@string/title_activity_conversations"
android:launchMode="singleTask"
android:windowSoftInputMode="stateHidden" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name="eu.siacs.conversations.ui.StartConversationActivity"
android:configChanges="orientation|screenSize"
android:label="@string/title_activity_start_conversation"
android:logo="@drawable/ic_activity" >
<intent-filter>
<action android:name="android.intent.action.SENDTO" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="imto" />
<data android:host="jabber" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="xmpp" />
</intent-filter>
</activity>
<activity
android:name="eu.siacs.conversations.ui.SettingsActivity"
android:label="@string/title_activity_settings" >
</activity>
<activity
android:name="eu.siacs.conversations.ui.ChooseContactActivity"
android:label="@string/title_activity_choose_contact" >
</activity>
<activity
android:name="eu.siacs.conversations.ui.ManageAccountActivity"
android:configChanges="orientation|screenSize"
android:label="@string/title_activity_manage_accounts" >
</activity>
<activity
android:name="eu.siacs.conversations.ui.EditAccountActivity"
android:windowSoftInputMode="stateHidden|adjustResize" >
</activity>
<activity
android:name="eu.siacs.conversations.ui.ConferenceDetailsActivity"
android:label="@string/title_activity_conference_details"
android:windowSoftInputMode="stateHidden" >
</activity>
<activity
android:name="eu.siacs.conversations.ui.ContactDetailsActivity"
android:label="@string/title_activity_contact_details"
android:windowSoftInputMode="stateHidden" >
</activity>
<activity
android:name="eu.siacs.conversations.ui.PublishProfilePictureActivity"
android:label="@string/mgmt_account_publish_avatar"
android:windowSoftInputMode="stateHidden" >
</activity>
<activity
android:name="eu.siacs.conversations.ui.ShareWithActivity"
android:label="@string/title_activity_conversations" >
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="image/*" />
</intent-filter>
</activity>
<activity android:name="de.duenndns.ssl.MemorizingActivity" />
</application>
</manifest>

25
conversations/src/main/java/eu/siacs/conversations/Config.java

@ -0,0 +1,25 @@ @@ -0,0 +1,25 @@
package eu.siacs.conversations;
import android.graphics.Bitmap;
public final class Config {
public static final String LOGTAG = "conversations";
public static final int PING_MAX_INTERVAL = 300;
public static final int PING_MIN_INTERVAL = 30;
public static final int PING_TIMEOUT = 10;
public static final int CONNECT_TIMEOUT = 90;
public static final int CARBON_GRACE_PERIOD = 60;
public static final int AVATAR_SIZE = 192;
public static final Bitmap.CompressFormat AVATAR_FORMAT = Bitmap.CompressFormat.WEBP;
public static final int MESSAGE_MERGE_WINDOW = 20;
public static final boolean PARSE_EMOTICONS = false;
private Config() {
}
}

231
conversations/src/main/java/eu/siacs/conversations/crypto/OtrEngine.java

@ -0,0 +1,231 @@ @@ -0,0 +1,231 @@
package eu.siacs.conversations.crypto;
import java.math.BigInteger;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.DSAPrivateKeySpec;
import java.security.spec.DSAPublicKeySpec;
import java.security.spec.InvalidKeySpecException;
import org.json.JSONException;
import org.json.JSONObject;
import android.util.Log;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.xmpp.stanzas.MessagePacket;
import net.java.otr4j.OtrEngineHost;
import net.java.otr4j.OtrException;
import net.java.otr4j.OtrPolicy;
import net.java.otr4j.OtrPolicyImpl;
import net.java.otr4j.session.InstanceTag;
import net.java.otr4j.session.SessionID;
public class OtrEngine implements OtrEngineHost {
private Account account;
private OtrPolicy otrPolicy;
private KeyPair keyPair;
private XmppConnectionService mXmppConnectionService;
public OtrEngine(XmppConnectionService service, Account account) {
this.account = account;
this.otrPolicy = new OtrPolicyImpl();
this.otrPolicy.setAllowV1(false);
this.otrPolicy.setAllowV2(true);
this.otrPolicy.setAllowV3(true);
this.keyPair = loadKey(account.getKeys());
this.mXmppConnectionService = service;
}
private KeyPair loadKey(JSONObject keys) {
if (keys == null) {
return null;
}
try {
BigInteger x = new BigInteger(keys.getString("otr_x"), 16);
BigInteger y = new BigInteger(keys.getString("otr_y"), 16);
BigInteger p = new BigInteger(keys.getString("otr_p"), 16);
BigInteger q = new BigInteger(keys.getString("otr_q"), 16);
BigInteger g = new BigInteger(keys.getString("otr_g"), 16);
KeyFactory keyFactory = KeyFactory.getInstance("DSA");
DSAPublicKeySpec pubKeySpec = new DSAPublicKeySpec(y, p, q, g);
DSAPrivateKeySpec privateKeySpec = new DSAPrivateKeySpec(x, p, q, g);
PublicKey publicKey = keyFactory.generatePublic(pubKeySpec);
PrivateKey privateKey = keyFactory.generatePrivate(privateKeySpec);
return new KeyPair(publicKey, privateKey);
} catch (JSONException e) {
return null;
} catch (NoSuchAlgorithmException e) {
return null;
} catch (InvalidKeySpecException e) {
return null;
}
}
private void saveKey() {
PublicKey publicKey = keyPair.getPublic();
PrivateKey privateKey = keyPair.getPrivate();
KeyFactory keyFactory;
try {
keyFactory = KeyFactory.getInstance("DSA");
DSAPrivateKeySpec privateKeySpec = keyFactory.getKeySpec(
privateKey, DSAPrivateKeySpec.class);
DSAPublicKeySpec publicKeySpec = keyFactory.getKeySpec(publicKey,
DSAPublicKeySpec.class);
this.account.setKey("otr_x", privateKeySpec.getX().toString(16));
this.account.setKey("otr_g", privateKeySpec.getG().toString(16));
this.account.setKey("otr_p", privateKeySpec.getP().toString(16));
this.account.setKey("otr_q", privateKeySpec.getQ().toString(16));
this.account.setKey("otr_y", publicKeySpec.getY().toString(16));
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (InvalidKeySpecException e) {
e.printStackTrace();
}
}
@Override
public void askForSecret(SessionID arg0, InstanceTag arg1, String arg2) {
// TODO Auto-generated method stub
}
@Override
public void finishedSessionMessage(SessionID arg0, String arg1)
throws OtrException {
}
@Override
public String getFallbackMessage(SessionID arg0) {
return "I would like to start a private (OTR encrypted) conversation but your client doesnโ€™t seem to support that";
}
@Override
public byte[] getLocalFingerprintRaw(SessionID arg0) {
// TODO Auto-generated method stub
return null;
}
public PublicKey getPublicKey() {
if (this.keyPair == null) {
return null;
}
return this.keyPair.getPublic();
}
@Override
public KeyPair getLocalKeyPair(SessionID arg0) throws OtrException {
if (this.keyPair == null) {
KeyPairGenerator kg;
try {
kg = KeyPairGenerator.getInstance("DSA");
this.keyPair = kg.genKeyPair();
this.saveKey();
mXmppConnectionService.databaseBackend.updateAccount(account);
} catch (NoSuchAlgorithmException e) {
Log.d(Config.LOGTAG,
"error generating key pair " + e.getMessage());
}
}
return this.keyPair;
}
@Override
public String getReplyForUnreadableMessage(SessionID arg0) {
// TODO Auto-generated method stub
return null;
}
@Override
public OtrPolicy getSessionPolicy(SessionID arg0) {
return otrPolicy;
}
@Override
public void injectMessage(SessionID session, String body)
throws OtrException {
MessagePacket packet = new MessagePacket();
packet.setFrom(account.getFullJid());
if (session.getUserID().isEmpty()) {
packet.setTo(session.getAccountID());
} else {
packet.setTo(session.getAccountID() + "/" + session.getUserID());
}
packet.setBody(body);
packet.addChild("private", "urn:xmpp:carbons:2");
packet.addChild("no-copy", "urn:xmpp:hints");
packet.setType(MessagePacket.TYPE_CHAT);
account.getXmppConnection().sendMessagePacket(packet);
}
@Override
public void messageFromAnotherInstanceReceived(SessionID id) {
Log.d(Config.LOGTAG,
"unreadable message received from " + id.getAccountID());
}
@Override
public void multipleInstancesDetected(SessionID arg0) {
// TODO Auto-generated method stub
}
@Override
public void requireEncryptedMessage(SessionID arg0, String arg1)
throws OtrException {
// TODO Auto-generated method stub
}
@Override
public void showError(SessionID arg0, String arg1) throws OtrException {
// TODO Auto-generated method stub
}
@Override
public void smpAborted(SessionID arg0) throws OtrException {
// TODO Auto-generated method stub
}
@Override
public void smpError(SessionID arg0, int arg1, boolean arg2)
throws OtrException {
throw new OtrException(new Exception("smp error"));
}
@Override
public void unencryptedMessageReceived(SessionID arg0, String arg1)
throws OtrException {
throw new OtrException(new Exception("unencrypted message received"));
}
@Override
public void unreadableMessageReceived(SessionID arg0) throws OtrException {
throw new OtrException(new Exception("unreadable message received"));
}
@Override
public void unverify(SessionID arg0, String arg1) {
// TODO Auto-generated method stub
}
@Override
public void verify(SessionID arg0, String arg1, boolean arg2) {
// TODO Auto-generated method stub
}
}

385
conversations/src/main/java/eu/siacs/conversations/crypto/PgpEngine.java

@ -0,0 +1,385 @@ @@ -0,0 +1,385 @@
package eu.siacs.conversations.crypto;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import org.openintents.openpgp.OpenPgpError;
import org.openintents.openpgp.OpenPgpSignatureResult;
import org.openintents.openpgp.util.OpenPgpApi;
import org.openintents.openpgp.util.OpenPgpApi.IOpenPgpCallback;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Contact;
import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.entities.DownloadableFile;
import eu.siacs.conversations.entities.Message;
import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.ui.UiCallback;
import android.app.PendingIntent;
import android.content.Intent;
import android.graphics.BitmapFactory;
import android.util.Log;
public class PgpEngine {
private OpenPgpApi api;
private XmppConnectionService mXmppConnectionService;
public PgpEngine(OpenPgpApi api, XmppConnectionService service) {
this.api = api;
this.mXmppConnectionService = service;
}
public void decrypt(final Message message,
final UiCallback<Message> callback) {
Log.d(Config.LOGTAG, "decrypting message " + message.getUuid());
Intent params = new Intent();
params.setAction(OpenPgpApi.ACTION_DECRYPT_VERIFY);
params.putExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME, message
.getConversation().getAccount().getJid());
if (message.getType() == Message.TYPE_TEXT) {
InputStream is = new ByteArrayInputStream(message.getBody()
.getBytes());
final OutputStream os = new ByteArrayOutputStream();
api.executeApiAsync(params, is, os, new IOpenPgpCallback() {
@Override
public void onReturn(Intent result) {
switch (result.getIntExtra(OpenPgpApi.RESULT_CODE,
OpenPgpApi.RESULT_CODE_ERROR)) {
case OpenPgpApi.RESULT_CODE_SUCCESS:
try {
os.flush();
if (message.getEncryption() == Message.ENCRYPTION_PGP) {
message.setBody(os.toString());
message.setEncryption(Message.ENCRYPTION_DECRYPTED);
callback.success(message);
}
} catch (IOException e) {
callback.error(R.string.openpgp_error, message);
return;
}
return;
case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
callback.userInputRequried((PendingIntent) result
.getParcelableExtra(OpenPgpApi.RESULT_INTENT),
message);
return;
case OpenPgpApi.RESULT_CODE_ERROR:
OpenPgpError error = result
.getParcelableExtra(OpenPgpApi.RESULT_ERROR);
Log.d(Config.LOGTAG,
"openpgp error: " + error.getMessage());
callback.error(R.string.openpgp_error, message);
return;
default:
return;
}
}
});
} else if (message.getType() == Message.TYPE_IMAGE) {
try {
final DownloadableFile inputFile = this.mXmppConnectionService
.getFileBackend().getFile(message, false);
final DownloadableFile outputFile = this.mXmppConnectionService
.getFileBackend().getFile(message, true);
outputFile.createNewFile();
InputStream is = new FileInputStream(inputFile);
OutputStream os = new FileOutputStream(outputFile);
api.executeApiAsync(params, is, os, new IOpenPgpCallback() {
@Override
public void onReturn(Intent result) {
switch (result.getIntExtra(OpenPgpApi.RESULT_CODE,
OpenPgpApi.RESULT_CODE_ERROR)) {
case OpenPgpApi.RESULT_CODE_SUCCESS:
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(
outputFile.getAbsolutePath(), options);
int imageHeight = options.outHeight;
int imageWidth = options.outWidth;
message.setBody(Long.toString(outputFile.getSize())
+ ',' + imageWidth + ',' + imageHeight);
message.setEncryption(Message.ENCRYPTION_DECRYPTED);
PgpEngine.this.mXmppConnectionService
.updateMessage(message);
;
callback.success(message);
return;
case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
callback.userInputRequried(
(PendingIntent) result
.getParcelableExtra(OpenPgpApi.RESULT_INTENT),
message);
return;
case OpenPgpApi.RESULT_CODE_ERROR:
callback.error(R.string.openpgp_error, message);
return;
default:
return;
}
}
});
} catch (FileNotFoundException e) {
callback.error(R.string.error_decrypting_file, message);
} catch (IOException e) {
callback.error(R.string.error_decrypting_file, message);
}
}
}
public void encrypt(final Message message,
final UiCallback<Message> callback) {
Intent params = new Intent();
params.setAction(OpenPgpApi.ACTION_ENCRYPT);
if (message.getConversation().getMode() == Conversation.MODE_SINGLE) {
long[] keys = { message.getConversation().getContact()
.getPgpKeyId() };
params.putExtra(OpenPgpApi.EXTRA_KEY_IDS, keys);
} else {
params.putExtra(OpenPgpApi.EXTRA_KEY_IDS, message.getConversation()
.getMucOptions().getPgpKeyIds());
}
params.putExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME, message
.getConversation().getAccount().getJid());
if (message.getType() == Message.TYPE_TEXT) {
params.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true);
InputStream is = new ByteArrayInputStream(message.getBody()
.getBytes());
final OutputStream os = new ByteArrayOutputStream();
api.executeApiAsync(params, is, os, new IOpenPgpCallback() {
@Override
public void onReturn(Intent result) {
switch (result.getIntExtra(OpenPgpApi.RESULT_CODE,
OpenPgpApi.RESULT_CODE_ERROR)) {
case OpenPgpApi.RESULT_CODE_SUCCESS:
try {
os.flush();
StringBuilder encryptedMessageBody = new StringBuilder();
String[] lines = os.toString().split("\n");
for (int i = 2; i < lines.length - 1; ++i) {
if (!lines[i].contains("Version")) {
encryptedMessageBody.append(lines[i].trim());
}
}
message.setEncryptedBody(encryptedMessageBody
.toString());
callback.success(message);
} catch (IOException e) {
callback.error(R.string.openpgp_error, message);
}
break;
case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
callback.userInputRequried((PendingIntent) result
.getParcelableExtra(OpenPgpApi.RESULT_INTENT),
message);
break;
case OpenPgpApi.RESULT_CODE_ERROR:
callback.error(R.string.openpgp_error, message);
break;
}
}
});
} else if (message.getType() == Message.TYPE_IMAGE) {
try {
DownloadableFile inputFile = this.mXmppConnectionService
.getFileBackend().getFile(message, true);
DownloadableFile outputFile = this.mXmppConnectionService
.getFileBackend().getFile(message, false);
outputFile.createNewFile();
InputStream is = new FileInputStream(inputFile);
OutputStream os = new FileOutputStream(outputFile);
api.executeApiAsync(params, is, os, new IOpenPgpCallback() {
@Override
public void onReturn(Intent result) {
switch (result.getIntExtra(OpenPgpApi.RESULT_CODE,
OpenPgpApi.RESULT_CODE_ERROR)) {
case OpenPgpApi.RESULT_CODE_SUCCESS:
callback.success(message);
break;
case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
callback.userInputRequried(
(PendingIntent) result
.getParcelableExtra(OpenPgpApi.RESULT_INTENT),
message);
break;
case OpenPgpApi.RESULT_CODE_ERROR:
callback.error(R.string.openpgp_error, message);
break;
}
}
});
} catch (FileNotFoundException e) {
Log.d(Config.LOGTAG, "file not found: " + e.getMessage());
} catch (IOException e) {
Log.d(Config.LOGTAG, "io exception during file encrypt");
}
}
}
public long fetchKeyId(Account account, String status, String signature) {
if ((signature == null) || (api == null)) {
return 0;
}
if (status == null) {
status = "";
}
StringBuilder pgpSig = new StringBuilder();
pgpSig.append("-----BEGIN PGP SIGNED MESSAGE-----");
pgpSig.append('\n');
pgpSig.append('\n');
pgpSig.append(status);
pgpSig.append('\n');
pgpSig.append("-----BEGIN PGP SIGNATURE-----");
pgpSig.append('\n');
pgpSig.append('\n');
pgpSig.append(signature.replace("\n", "").trim());
pgpSig.append('\n');
pgpSig.append("-----END PGP SIGNATURE-----");
Intent params = new Intent();
params.setAction(OpenPgpApi.ACTION_DECRYPT_VERIFY);
params.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true);
params.putExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME, account.getJid());
InputStream is = new ByteArrayInputStream(pgpSig.toString().getBytes());
ByteArrayOutputStream os = new ByteArrayOutputStream();
Intent result = api.executeApi(params, is, os);
switch (result.getIntExtra(OpenPgpApi.RESULT_CODE,
OpenPgpApi.RESULT_CODE_ERROR)) {
case OpenPgpApi.RESULT_CODE_SUCCESS:
OpenPgpSignatureResult sigResult = result
.getParcelableExtra(OpenPgpApi.RESULT_SIGNATURE);
if (sigResult != null) {
return sigResult.getKeyId();
} else {
return 0;
}
case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
return 0;
case OpenPgpApi.RESULT_CODE_ERROR:
Log.d(Config.LOGTAG,
"openpgp error: "
+ ((OpenPgpError) result
.getParcelableExtra(OpenPgpApi.RESULT_ERROR))
.getMessage());
return 0;
}
return 0;
}
public void generateSignature(final Account account, String status,
final UiCallback<Account> callback) {
Intent params = new Intent();
params.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true);
params.setAction(OpenPgpApi.ACTION_SIGN);
params.putExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME, account.getJid());
InputStream is = new ByteArrayInputStream(status.getBytes());
final OutputStream os = new ByteArrayOutputStream();
api.executeApiAsync(params, is, os, new IOpenPgpCallback() {
@Override
public void onReturn(Intent result) {
switch (result.getIntExtra(OpenPgpApi.RESULT_CODE, 0)) {
case OpenPgpApi.RESULT_CODE_SUCCESS:
StringBuilder signatureBuilder = new StringBuilder();
try {
os.flush();
String[] lines = os.toString().split("\n");
boolean sig = false;
for (String line : lines) {
if (sig) {
if (line.contains("END PGP SIGNATURE")) {
sig = false;
} else {
if (!line.contains("Version")) {
signatureBuilder.append(line.trim());
}
}
}
if (line.contains("BEGIN PGP SIGNATURE")) {
sig = true;
}
}
} catch (IOException e) {
callback.error(R.string.openpgp_error, account);
return;
}
account.setKey("pgp_signature", signatureBuilder.toString());
callback.success(account);
return;
case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
callback.userInputRequried((PendingIntent) result
.getParcelableExtra(OpenPgpApi.RESULT_INTENT),
account);
return;
case OpenPgpApi.RESULT_CODE_ERROR:
callback.error(R.string.openpgp_error, account);
return;
}
}
});
}
public void hasKey(final Contact contact, final UiCallback<Contact> callback) {
Intent params = new Intent();
params.setAction(OpenPgpApi.ACTION_GET_KEY);
params.putExtra(OpenPgpApi.EXTRA_KEY_ID, contact.getPgpKeyId());
params.putExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME, contact.getAccount()
.getJid());
api.executeApiAsync(params, null, null, new IOpenPgpCallback() {
@Override
public void onReturn(Intent result) {
switch (result.getIntExtra(OpenPgpApi.RESULT_CODE, 0)) {
case OpenPgpApi.RESULT_CODE_SUCCESS:
callback.success(contact);
return;
case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
callback.userInputRequried((PendingIntent) result
.getParcelableExtra(OpenPgpApi.RESULT_INTENT),
contact);
return;
case OpenPgpApi.RESULT_CODE_ERROR:
callback.error(R.string.openpgp_error, contact);
return;
}
}
});
}
public PendingIntent getIntentForKey(Contact contact) {
Intent params = new Intent();
params.setAction(OpenPgpApi.ACTION_GET_KEY);
params.putExtra(OpenPgpApi.EXTRA_KEY_ID, contact.getPgpKeyId());
params.putExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME, contact.getAccount()
.getJid());
Intent result = api.executeApi(params, null, null);
return (PendingIntent) result
.getParcelableExtra(OpenPgpApi.RESULT_INTENT);
}
public PendingIntent getIntentForKey(Account account, long pgpKeyId) {
Intent params = new Intent();
params.setAction(OpenPgpApi.ACTION_GET_KEY);
params.putExtra(OpenPgpApi.EXTRA_KEY_ID, pgpKeyId);
params.putExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME, account.getJid());
Intent result = api.executeApi(params, null, null);
return (PendingIntent) result
.getParcelableExtra(OpenPgpApi.RESULT_INTENT);
}
}

21
conversations/src/main/java/eu/siacs/conversations/entities/AbstractEntity.java

@ -0,0 +1,21 @@ @@ -0,0 +1,21 @@
package eu.siacs.conversations.entities;
import android.content.ContentValues;
public abstract class AbstractEntity {
public static final String UUID = "uuid";
protected String uuid;
public String getUuid() {
return this.uuid;
}
public abstract ContentValues getContentValues();
public boolean equals(AbstractEntity entity) {
return this.getUuid().equals(entity.getUuid());
}
}

399
conversations/src/main/java/eu/siacs/conversations/entities/Account.java

@ -0,0 +1,399 @@ @@ -0,0 +1,399 @@
package eu.siacs.conversations.entities;
import java.security.interfaces.DSAPublicKey;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.CopyOnWriteArrayList;
import net.java.otr4j.crypto.OtrCryptoEngineImpl;
import net.java.otr4j.crypto.OtrCryptoException;
import org.json.JSONException;
import org.json.JSONObject;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
import eu.siacs.conversations.crypto.OtrEngine;
import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.xmpp.XmppConnection;
import android.content.ContentValues;
import android.database.Cursor;
import android.os.SystemClock;
public class Account extends AbstractEntity {
public static final String TABLENAME = "accounts";
public static final String USERNAME = "username";
public static final String SERVER = "server";
public static final String PASSWORD = "password";
public static final String OPTIONS = "options";
public static final String ROSTERVERSION = "rosterversion";
public static final String KEYS = "keys";
public static final String AVATAR = "avatar";
public static final int OPTION_USETLS = 0;
public static final int OPTION_DISABLED = 1;
public static final int OPTION_REGISTER = 2;
public static final int OPTION_USECOMPRESSION = 3;
public static final int STATUS_CONNECTING = 0;
public static final int STATUS_DISABLED = -2;
public static final int STATUS_OFFLINE = -1;
public static final int STATUS_ONLINE = 1;
public static final int STATUS_NO_INTERNET = 2;
public static final int STATUS_UNAUTHORIZED = 3;
public static final int STATUS_SERVER_NOT_FOUND = 5;
public static final int STATUS_REGISTRATION_FAILED = 7;
public static final int STATUS_REGISTRATION_CONFLICT = 8;
public static final int STATUS_REGISTRATION_SUCCESSFULL = 9;
public static final int STATUS_REGISTRATION_NOT_SUPPORTED = 10;
protected String username;
protected String server;
protected String password;
protected int options = 0;
protected String rosterVersion;
protected String resource = "mobile";
protected int status = -1;
protected JSONObject keys = new JSONObject();
protected String avatar;
protected boolean online = false;
private OtrEngine otrEngine = null;
private XmppConnection xmppConnection = null;
private Presences presences = new Presences();
private long mEndGracePeriod = 0L;
private String otrFingerprint;
private Roster roster = null;
private List<Bookmark> bookmarks = new CopyOnWriteArrayList<Bookmark>();
public List<Conversation> pendingConferenceJoins = new CopyOnWriteArrayList<Conversation>();
public List<Conversation> pendingConferenceLeaves = new CopyOnWriteArrayList<Conversation>();
public Account() {
this.uuid = "0";
}
public Account(String username, String server, String password) {
this(java.util.UUID.randomUUID().toString(), username, server,
password, 0, null, "", null);
}
public Account(String uuid, String username, String server,
String password, int options, String rosterVersion, String keys,
String avatar) {
this.uuid = uuid;
this.username = username;
this.server = server;
this.password = password;
this.options = options;
this.rosterVersion = rosterVersion;
try {
this.keys = new JSONObject(keys);
} catch (JSONException e) {
}
this.avatar = avatar;
}
public boolean isOptionSet(int option) {
return ((options & (1 << option)) != 0);
}
public void setOption(int option, boolean value) {
if (value) {
this.options |= 1 << option;
} else {
this.options &= ~(1 << option);
}
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getServer() {
return server;
}
public void setServer(String server) {
this.server = server;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public void setStatus(int status) {
this.status = status;
}
public int getStatus() {
if (isOptionSet(OPTION_DISABLED)) {
return STATUS_DISABLED;
} else {
return this.status;
}
}
public boolean errorStatus() {
int s = getStatus();
return (s == STATUS_REGISTRATION_FAILED
|| s == STATUS_REGISTRATION_CONFLICT
|| s == STATUS_REGISTRATION_NOT_SUPPORTED
|| s == STATUS_SERVER_NOT_FOUND || s == STATUS_UNAUTHORIZED);
}
public boolean hasErrorStatus() {
if (getXmppConnection() == null) {
return false;
} else {
return getStatus() > STATUS_NO_INTERNET
&& (getXmppConnection().getAttempt() >= 2);
}
}
public void setResource(String resource) {
this.resource = resource;
}
public String getResource() {
return this.resource;
}
public String getJid() {
return username.toLowerCase(Locale.getDefault()) + "@"
+ server.toLowerCase(Locale.getDefault());
}
public JSONObject getKeys() {
return keys;
}
public String getSSLFingerprint() {
if (keys.has("ssl_cert")) {
try {
return keys.getString("ssl_cert");
} catch (JSONException e) {
return null;
}
} else {
return null;
}
}
public void setSSLCertFingerprint(String fingerprint) {
this.setKey("ssl_cert", fingerprint);
}
public boolean setKey(String keyName, String keyValue) {
try {
this.keys.put(keyName, keyValue);
return true;
} catch (JSONException e) {
return false;
}
}
@Override
public ContentValues getContentValues() {
ContentValues values = new ContentValues();
values.put(UUID, uuid);
values.put(USERNAME, username);
values.put(SERVER, server);
values.put(PASSWORD, password);
values.put(OPTIONS, options);
values.put(KEYS, this.keys.toString());
values.put(ROSTERVERSION, rosterVersion);
values.put(AVATAR, avatar);
return values;
}
public static Account fromCursor(Cursor cursor) {
return new Account(cursor.getString(cursor.getColumnIndex(UUID)),
cursor.getString(cursor.getColumnIndex(USERNAME)),
cursor.getString(cursor.getColumnIndex(SERVER)),
cursor.getString(cursor.getColumnIndex(PASSWORD)),
cursor.getInt(cursor.getColumnIndex(OPTIONS)),
cursor.getString(cursor.getColumnIndex(ROSTERVERSION)),
cursor.getString(cursor.getColumnIndex(KEYS)),
cursor.getString(cursor.getColumnIndex(AVATAR)));
}
public OtrEngine getOtrEngine(XmppConnectionService context) {
if (otrEngine == null) {
otrEngine = new OtrEngine(context, this);
}
return this.otrEngine;
}
public XmppConnection getXmppConnection() {
return this.xmppConnection;
}
public void setXmppConnection(XmppConnection connection) {
this.xmppConnection = connection;
}
public String getFullJid() {
return this.getJid() + "/" + this.resource;
}
public String getOtrFingerprint() {
if (this.otrFingerprint == null) {
try {
DSAPublicKey pubkey = (DSAPublicKey) this.otrEngine
.getPublicKey();
if (pubkey == null) {
return null;
}
StringBuilder builder = new StringBuilder(
new OtrCryptoEngineImpl().getFingerprint(pubkey));
builder.insert(8, " ");
builder.insert(17, " ");
builder.insert(26, " ");
builder.insert(35, " ");
this.otrFingerprint = builder.toString();
} catch (OtrCryptoException e) {
}
}
return this.otrFingerprint;
}
public String getRosterVersion() {
if (this.rosterVersion == null) {
return "";
} else {
return this.rosterVersion;
}
}
public void setRosterVersion(String version) {
this.rosterVersion = version;
}
public String getOtrFingerprint(XmppConnectionService service) {
this.getOtrEngine(service);
return this.getOtrFingerprint();
}
public void updatePresence(String resource, int status) {
this.presences.updatePresence(resource, status);
}
public void removePresence(String resource) {
this.presences.removePresence(resource);
}
public void clearPresences() {
this.presences = new Presences();
}
public int countPresences() {
return this.presences.size();
}
public String getPgpSignature() {
if (keys.has("pgp_signature")) {
try {
return keys.getString("pgp_signature");
} catch (JSONException e) {
return null;
}
} else {
return null;
}
}
public Roster getRoster() {
if (this.roster == null) {
this.roster = new Roster(this);
}
return this.roster;
}
public void setBookmarks(List<Bookmark> bookmarks) {
this.bookmarks = bookmarks;
}
public List<Bookmark> getBookmarks() {
return this.bookmarks;
}
public boolean hasBookmarkFor(String conferenceJid) {
for (Bookmark bmark : this.bookmarks) {
if (bmark.getJid().equals(conferenceJid)) {
return true;
}
}
return false;
}
public boolean setAvatar(String filename) {
if (this.avatar != null && this.avatar.equals(filename)) {
return false;
} else {
this.avatar = filename;
return true;
}
}
public String getAvatar() {
return this.avatar;
}
public int getReadableStatusId() {
switch (getStatus()) {
case Account.STATUS_DISABLED:
return R.string.account_status_disabled;
case Account.STATUS_ONLINE:
return R.string.account_status_online;
case Account.STATUS_CONNECTING:
return R.string.account_status_connecting;
case Account.STATUS_OFFLINE:
return R.string.account_status_offline;
case Account.STATUS_UNAUTHORIZED:
return R.string.account_status_unauthorized;
case Account.STATUS_SERVER_NOT_FOUND:
return R.string.account_status_not_found;
case Account.STATUS_NO_INTERNET:
return R.string.account_status_no_internet;
case Account.STATUS_REGISTRATION_FAILED:
return R.string.account_status_regis_fail;
case Account.STATUS_REGISTRATION_CONFLICT:
return R.string.account_status_regis_conflict;
case Account.STATUS_REGISTRATION_SUCCESSFULL:
return R.string.account_status_regis_success;
case Account.STATUS_REGISTRATION_NOT_SUPPORTED:
return R.string.account_status_regis_not_sup;
default:
return R.string.account_status_unknown;
}
}
public void activateGracePeriod() {
this.mEndGracePeriod = SystemClock.elapsedRealtime()
+ (Config.CARBON_GRACE_PERIOD * 1000);
}
public void deactivateGracePeriod() {
this.mEndGracePeriod = 0L;
}
public boolean inGracePeriod() {
return SystemClock.elapsedRealtime() < this.mEndGracePeriod;
}
}

137
conversations/src/main/java/eu/siacs/conversations/entities/Bookmark.java

@ -0,0 +1,137 @@ @@ -0,0 +1,137 @@
package eu.siacs.conversations.entities;
import java.util.Locale;
import eu.siacs.conversations.xml.Element;
public class Bookmark extends Element implements ListItem {
private Account account;
private Conversation mJoinedConversation;
public Bookmark(Account account, String jid) {
super("conference");
this.setAttribute("jid", jid);
this.account = account;
}
private Bookmark(Account account) {
super("conference");
this.account = account;
}
public static Bookmark parse(Element element, Account account) {
Bookmark bookmark = new Bookmark(account);
bookmark.setAttributes(element.getAttributes());
bookmark.setChildren(element.getChildren());
return bookmark;
}
public void setAutojoin(boolean autojoin) {
if (autojoin) {
this.setAttribute("autojoin", "true");
} else {
this.setAttribute("autojoin", "false");
}
}
public void setName(String name) {
this.name = name;
}
public void setNick(String nick) {
Element element = this.findChild("nick");
if (element == null) {
element = this.addChild("nick");
}
element.setContent(nick);
}
public void setPassword(String password) {
Element element = this.findChild("password");
if (element != null) {
element.setContent(password);
}
}
@Override
public int compareTo(ListItem another) {
return this.getDisplayName().compareToIgnoreCase(
another.getDisplayName());
}
@Override
public String getDisplayName() {
if (this.mJoinedConversation != null
&& (this.mJoinedConversation.getMucOptions().getSubject() != null)) {
return this.mJoinedConversation.getMucOptions().getSubject();
} else if (getName() != null) {
return getName();
} else {
return this.getJid().split("@")[0];
}
}
@Override
public String getJid() {
String jid = this.getAttribute("jid");
if (jid != null) {
return jid.toLowerCase(Locale.US);
} else {
return null;
}