
commit
64bdd7e731
323 changed files with 25921 additions and 0 deletions
@ -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 |
@ -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 |
@ -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() |
||||
} |
||||
} |
@ -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') |
||||
} |
@ -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> |
@ -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() { |
||||
|
||||
} |
||||
} |
@ -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
|
||||
|
||||
} |
||||
|
||||
} |
@ -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); |
||||
} |
||||
} |
@ -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()); |
||||
} |
||||
|
||||
} |
@ -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; |
||||
} |
||||
} |
@ -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; |
||||
} |
||||