mirror of
https://github.com/signalapp/Signal-Server
synced 2026-04-26 05:18:06 +01:00
New API to support multiple accounts per # (FREEBIE)
This commit is contained in:
@@ -27,27 +27,36 @@ public class Account implements Serializable {
|
||||
|
||||
private long id;
|
||||
private String number;
|
||||
private long deviceId;
|
||||
private String hashedAuthenticationToken;
|
||||
private String salt;
|
||||
private String signalingKey;
|
||||
/**
|
||||
* In order for us to tell a client that an account is "inactive" (ie go use SMS for transport), we check that all
|
||||
* non-fetching Accounts don't have push registrations. In this way, we can ensure that we have some form of transport
|
||||
* available for all Accounts on all "active" numbers.
|
||||
*/
|
||||
private String gcmRegistrationId;
|
||||
private String apnRegistrationId;
|
||||
private boolean supportsSms;
|
||||
private boolean fetchesMessages;
|
||||
|
||||
public Account() {}
|
||||
|
||||
public Account(long id, String number, String hashedAuthenticationToken, String salt,
|
||||
public Account(long id, String number, long deviceId, String hashedAuthenticationToken, String salt,
|
||||
String signalingKey, String gcmRegistrationId, String apnRegistrationId,
|
||||
boolean supportsSms)
|
||||
boolean supportsSms, boolean fetchesMessages)
|
||||
{
|
||||
this.id = id;
|
||||
this.number = number;
|
||||
this.deviceId = deviceId;
|
||||
this.hashedAuthenticationToken = hashedAuthenticationToken;
|
||||
this.salt = salt;
|
||||
this.signalingKey = signalingKey;
|
||||
this.gcmRegistrationId = gcmRegistrationId;
|
||||
this.apnRegistrationId = apnRegistrationId;
|
||||
this.supportsSms = supportsSms;
|
||||
this.fetchesMessages = fetchesMessages;
|
||||
}
|
||||
|
||||
public String getApnRegistrationId() {
|
||||
@@ -74,6 +83,14 @@ public class Account implements Serializable {
|
||||
return number;
|
||||
}
|
||||
|
||||
public long getDeviceId() {
|
||||
return deviceId;
|
||||
}
|
||||
|
||||
public void setDeviceId(long deviceId) {
|
||||
this.deviceId = deviceId;
|
||||
}
|
||||
|
||||
public void setAuthenticationCredentials(AuthenticationCredentials credentials) {
|
||||
this.hashedAuthenticationToken = credentials.getHashedAuthenticationToken();
|
||||
this.salt = credentials.getSalt();
|
||||
@@ -106,4 +123,12 @@ public class Account implements Serializable {
|
||||
public void setId(long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public void setFetchesMessages(boolean fetchesMessages) {
|
||||
this.fetchesMessages = fetchesMessages;
|
||||
}
|
||||
|
||||
public boolean getFetchesMessages() {
|
||||
return fetchesMessages;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ import org.skife.jdbi.v2.sqlobject.SqlUpdate;
|
||||
import org.skife.jdbi.v2.sqlobject.Transaction;
|
||||
import org.skife.jdbi.v2.sqlobject.customizers.Mapper;
|
||||
import org.skife.jdbi.v2.tweak.ResultSetMapper;
|
||||
import org.whispersystems.textsecuregcm.util.NumberData;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.annotation.ElementType;
|
||||
@@ -42,50 +43,76 @@ import java.util.List;
|
||||
|
||||
public abstract class Accounts {
|
||||
|
||||
public static final String ID = "id";
|
||||
public static final String NUMBER = "number";
|
||||
public static final String AUTH_TOKEN = "auth_token";
|
||||
public static final String SALT = "salt";
|
||||
public static final String SIGNALING_KEY = "signaling_key";
|
||||
public static final String GCM_ID = "gcm_id";
|
||||
public static final String APN_ID = "apn_id";
|
||||
public static final String SUPPORTS_SMS = "supports_sms";
|
||||
public static final String ID = "id";
|
||||
public static final String NUMBER = "number";
|
||||
public static final String DEVICE_ID = "device_id";
|
||||
public static final String AUTH_TOKEN = "auth_token";
|
||||
public static final String SALT = "salt";
|
||||
public static final String SIGNALING_KEY = "signaling_key";
|
||||
public static final String GCM_ID = "gcm_id";
|
||||
public static final String APN_ID = "apn_id";
|
||||
public static final String FETCHES_MESSAGES = "fetches_messages";
|
||||
public static final String SUPPORTS_SMS = "supports_sms";
|
||||
|
||||
@SqlUpdate("INSERT INTO accounts (" + NUMBER + ", " + AUTH_TOKEN + ", " +
|
||||
SALT + ", " + SIGNALING_KEY + ", " + GCM_ID + ", " +
|
||||
APN_ID + ", " + SUPPORTS_SMS + ") " +
|
||||
"VALUES (:number, :auth_token, :salt, :signaling_key, :gcm_id, :apn_id, :supports_sms)")
|
||||
@SqlUpdate("INSERT INTO accounts (" + NUMBER + ", " + DEVICE_ID + ", " + AUTH_TOKEN + ", " +
|
||||
SALT + ", " + SIGNALING_KEY + ", " + FETCHES_MESSAGES + ", " +
|
||||
GCM_ID + ", " + APN_ID + ", " + SUPPORTS_SMS + ") " +
|
||||
"VALUES (:number, :device_id, :auth_token, :salt, :signaling_key, :fetches_messages, :gcm_id, :apn_id, :supports_sms)")
|
||||
@GetGeneratedKeys
|
||||
abstract long createStep(@AccountBinder Account account);
|
||||
abstract long insertStep(@AccountBinder Account account);
|
||||
|
||||
@SqlUpdate("DELETE FROM accounts WHERE number = :number")
|
||||
abstract void removeStep(@Bind("number") String number);
|
||||
@SqlQuery("SELECT " + DEVICE_ID + " FROM accounts WHERE " + NUMBER + " = :number ORDER BY " + DEVICE_ID + " DESC LIMIT 1 FOR UPDATE")
|
||||
abstract long getHighestDeviceId(@Bind("number") String number);
|
||||
|
||||
@Transaction(TransactionIsolationLevel.SERIALIZABLE)
|
||||
public long insert(@AccountBinder Account account) {
|
||||
account.setDeviceId(getHighestDeviceId(account.getNumber()) + 1);
|
||||
return insertStep(account);
|
||||
}
|
||||
|
||||
@SqlUpdate("DELETE FROM accounts WHERE " + NUMBER + " = :number RETURNING id")
|
||||
abstract void removeAccountsByNumber(@Bind("number") String number);
|
||||
|
||||
@SqlUpdate("UPDATE accounts SET " + AUTH_TOKEN + " = :auth_token, " + SALT + " = :salt, " +
|
||||
SIGNALING_KEY + " = :signaling_key, " + GCM_ID + " = :gcm_id, " +
|
||||
APN_ID + " = :apn_id, " + SUPPORTS_SMS + " = :supports_sms " +
|
||||
"WHERE " + NUMBER + " = :number")
|
||||
SIGNALING_KEY + " = :signaling_key, " + GCM_ID + " = :gcm_id, " + APN_ID + " = :apn_id, " +
|
||||
FETCHES_MESSAGES + " = :fetches_messages, " + SUPPORTS_SMS + " = :supports_sms " +
|
||||
"WHERE " + NUMBER + " = :number AND " + DEVICE_ID + " = :device_id")
|
||||
abstract void update(@AccountBinder Account account);
|
||||
|
||||
@Mapper(AccountMapper.class)
|
||||
@SqlQuery("SELECT * FROM accounts WHERE " + NUMBER + " = :number AND " + DEVICE_ID + " = :device_id")
|
||||
abstract Account get(@Bind("number") String number, @Bind("device_id") long deviceId);
|
||||
|
||||
@SqlQuery("SELECT COUNT(DISTINCT " + NUMBER + ") from accounts")
|
||||
abstract long getNumberCount();
|
||||
|
||||
private static final String NUMBER_DATA_QUERY = "SELECT number, COUNT(" +
|
||||
"CASE WHEN (" + GCM_ID + " IS NOT NULL OR " + APN_ID + " IS NOT NULL OR " + FETCHES_MESSAGES + " = 1) " +
|
||||
"THEN 1 ELSE 0 END) AS active, COUNT(" +
|
||||
"CASE WHEN " + SUPPORTS_SMS + " = 1 THEN 1 ELSE 0 END) AS " + SUPPORTS_SMS + " " +
|
||||
"FROM accounts";
|
||||
|
||||
@Mapper(NumberDataMapper.class)
|
||||
@SqlQuery(NUMBER_DATA_QUERY + " GROUP BY " + NUMBER + " OFFSET :offset LIMIT :limit")
|
||||
abstract List<NumberData> getAllNumbers(@Bind("offset") int offset, @Bind("limit") int length);
|
||||
|
||||
@Mapper(NumberDataMapper.class)
|
||||
@SqlQuery(NUMBER_DATA_QUERY + " GROUP BY " + NUMBER)
|
||||
public abstract Iterator<NumberData> getAllNumbers();
|
||||
|
||||
@Mapper(NumberDataMapper.class)
|
||||
@SqlQuery(NUMBER_DATA_QUERY + " WHERE " + NUMBER + " = :number GROUP BY " + NUMBER)
|
||||
abstract NumberData getNumberData(@Bind("number") String number);
|
||||
|
||||
@Mapper(AccountMapper.class)
|
||||
@SqlQuery("SELECT * FROM accounts WHERE " + NUMBER + " = :number")
|
||||
abstract Account get(@Bind("number") String number);
|
||||
public abstract List<Account> getAllByNumber(@Bind("number") String number);
|
||||
|
||||
@SqlQuery("SELECT COUNT(*) from accounts")
|
||||
abstract long getCount();
|
||||
|
||||
@Mapper(AccountMapper.class)
|
||||
@SqlQuery("SELECT * FROM accounts OFFSET :offset LIMIT :limit")
|
||||
abstract List<Account> getAll(@Bind("offset") int offset, @Bind("limit") int length);
|
||||
|
||||
@Mapper(AccountMapper.class)
|
||||
@SqlQuery("SELECT * FROM accounts")
|
||||
abstract Iterator<Account> getAll();
|
||||
|
||||
@Transaction(TransactionIsolationLevel.REPEATABLE_READ)
|
||||
public long create(Account account) {
|
||||
removeStep(account.getNumber());
|
||||
return createStep(account);
|
||||
@Transaction(TransactionIsolationLevel.SERIALIZABLE)
|
||||
public long insertClearingNumber(Account account) {
|
||||
removeAccountsByNumber(account.getNumber());
|
||||
account.setDeviceId(getHighestDeviceId(account.getNumber()) + 1);
|
||||
return insertStep(account);
|
||||
}
|
||||
|
||||
public static class AccountMapper implements ResultSetMapper<Account> {
|
||||
@@ -94,11 +121,21 @@ public abstract class Accounts {
|
||||
public Account map(int i, ResultSet resultSet, StatementContext statementContext)
|
||||
throws SQLException
|
||||
{
|
||||
return new Account(resultSet.getLong(ID), resultSet.getString(NUMBER),
|
||||
return new Account(resultSet.getLong(ID), resultSet.getString(NUMBER), resultSet.getLong(DEVICE_ID),
|
||||
resultSet.getString(AUTH_TOKEN), resultSet.getString(SALT),
|
||||
resultSet.getString(SIGNALING_KEY), resultSet.getString(GCM_ID),
|
||||
resultSet.getString(APN_ID),
|
||||
resultSet.getInt(SUPPORTS_SMS) == 1);
|
||||
resultSet.getInt(SUPPORTS_SMS) == 1, resultSet.getInt(FETCHES_MESSAGES) == 1);
|
||||
}
|
||||
}
|
||||
|
||||
public static class NumberDataMapper implements ResultSetMapper<NumberData> {
|
||||
|
||||
@Override
|
||||
public NumberData map(int i, ResultSet resultSet, StatementContext statementContext)
|
||||
throws SQLException
|
||||
{
|
||||
return new NumberData(resultSet.getString("number"), resultSet.getInt("active") != 0, resultSet.getInt(SUPPORTS_SMS) != 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,6 +154,7 @@ public abstract class Accounts {
|
||||
{
|
||||
sql.bind(ID, account.getId());
|
||||
sql.bind(NUMBER, account.getNumber());
|
||||
sql.bind(DEVICE_ID, account.getDeviceId());
|
||||
sql.bind(AUTH_TOKEN, account.getAuthenticationCredentials()
|
||||
.getHashedAuthenticationToken());
|
||||
sql.bind(SALT, account.getAuthenticationCredentials().getSalt());
|
||||
@@ -124,6 +162,7 @@ public abstract class Accounts {
|
||||
sql.bind(GCM_ID, account.getGcmRegistrationId());
|
||||
sql.bind(APN_ID, account.getApnRegistrationId());
|
||||
sql.bind(SUPPORTS_SMS, account.getSupportsSms() ? 1 : 0);
|
||||
sql.bind(FETCHES_MESSAGES, account.getFetchesMessages() ? 1 : 0);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ package org.whispersystems.textsecuregcm.storage;
|
||||
import com.google.common.base.Optional;
|
||||
import net.spy.memcached.MemcachedClient;
|
||||
import org.whispersystems.textsecuregcm.entities.ClientContact;
|
||||
import org.whispersystems.textsecuregcm.util.NumberData;
|
||||
import org.whispersystems.textsecuregcm.util.Util;
|
||||
|
||||
import java.util.Iterator;
|
||||
@@ -41,24 +42,36 @@ public class AccountsManager {
|
||||
}
|
||||
|
||||
public long getCount() {
|
||||
return accounts.getCount();
|
||||
return accounts.getNumberCount();
|
||||
}
|
||||
|
||||
public List<Account> getAll(int offset, int length) {
|
||||
return accounts.getAll(offset, length);
|
||||
public List<NumberData> getAllNumbers(int offset, int length) {
|
||||
return accounts.getAllNumbers(offset, length);
|
||||
}
|
||||
|
||||
public Iterator<Account> getAll() {
|
||||
return accounts.getAll();
|
||||
public Iterator<NumberData> getAllNumbers() {
|
||||
return accounts.getAllNumbers();
|
||||
}
|
||||
|
||||
public void create(Account account) {
|
||||
long id = accounts.create(account);
|
||||
|
||||
/** Creates a new Account and NumberData, clearing all existing accounts/data on the given number */
|
||||
public void createResetNumber(Account account) {
|
||||
long id = accounts.insertClearingNumber(account);
|
||||
account.setId(id);
|
||||
|
||||
if (memcachedClient != null) {
|
||||
memcachedClient.set(getKey(account.getNumber()), 0, account);
|
||||
memcachedClient.set(getKey(account.getNumber(), account.getDeviceId()), 0, account);
|
||||
}
|
||||
|
||||
updateDirectory(account);
|
||||
}
|
||||
|
||||
/** Creates a new Account for an existing NumberData (setting the deviceId) */
|
||||
public void createAccountOnExistingNumber(Account account) {
|
||||
long id = accounts.insert(account);
|
||||
account.setId(id);
|
||||
|
||||
if (memcachedClient != null) {
|
||||
memcachedClient.set(getKey(account.getNumber(), account.getDeviceId()), 0, account);
|
||||
}
|
||||
|
||||
updateDirectory(account);
|
||||
@@ -66,25 +79,25 @@ public class AccountsManager {
|
||||
|
||||
public void update(Account account) {
|
||||
if (memcachedClient != null) {
|
||||
memcachedClient.set(getKey(account.getNumber()), 0, account);
|
||||
memcachedClient.set(getKey(account.getNumber(), account.getDeviceId()), 0, account);
|
||||
}
|
||||
|
||||
accounts.update(account);
|
||||
updateDirectory(account);
|
||||
}
|
||||
|
||||
public Optional<Account> get(String number) {
|
||||
public Optional<Account> get(String number, long deviceId) {
|
||||
Account account = null;
|
||||
|
||||
if (memcachedClient != null) {
|
||||
account = (Account)memcachedClient.get(getKey(number));
|
||||
account = (Account)memcachedClient.get(getKey(number, deviceId));
|
||||
}
|
||||
|
||||
if (account == null) {
|
||||
account = accounts.get(number);
|
||||
account = accounts.get(number, deviceId);
|
||||
|
||||
if (account != null && memcachedClient != null) {
|
||||
memcachedClient.set(getKey(number), 0, account);
|
||||
memcachedClient.set(getKey(number, deviceId), 0, account);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,17 +105,31 @@ public class AccountsManager {
|
||||
else return Optional.absent();
|
||||
}
|
||||
|
||||
public List<Account> getAllByNumber(String number) {
|
||||
return accounts.getAllByNumber(number);
|
||||
}
|
||||
|
||||
private void updateDirectory(Account account) {
|
||||
if (account.getGcmRegistrationId() != null || account.getApnRegistrationId() != null) {
|
||||
boolean active = account.getFetchesMessages() ||
|
||||
!Util.isEmpty(account.getApnRegistrationId()) || !Util.isEmpty(account.getGcmRegistrationId());
|
||||
boolean supportsSms = account.getSupportsSms();
|
||||
|
||||
if (!active || !supportsSms) {
|
||||
NumberData numberData = accounts.getNumberData(account.getNumber());
|
||||
active = numberData.isActive();
|
||||
supportsSms = numberData.isSupportsSms();
|
||||
}
|
||||
|
||||
if (active) {
|
||||
byte[] token = Util.getContactToken(account.getNumber());
|
||||
ClientContact clientContact = new ClientContact(token, null, account.getSupportsSms());
|
||||
ClientContact clientContact = new ClientContact(token, null, supportsSms);
|
||||
directory.add(clientContact);
|
||||
} else {
|
||||
directory.remove(account.getNumber());
|
||||
}
|
||||
}
|
||||
|
||||
private String getKey(String number) {
|
||||
return Account.class.getSimpleName() + Account.MEMCACHE_VERION + number;
|
||||
private String getKey(String number, long accountId) {
|
||||
return Account.class.getSimpleName() + Account.MEMCACHE_VERION + number + accountId;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ import org.skife.jdbi.v2.sqlobject.Transaction;
|
||||
import org.skife.jdbi.v2.sqlobject.customizers.Mapper;
|
||||
import org.skife.jdbi.v2.tweak.ResultSetMapper;
|
||||
import org.whispersystems.textsecuregcm.entities.PreKey;
|
||||
import org.whispersystems.textsecuregcm.entities.UnstructuredPreKeyList;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.annotation.ElementType;
|
||||
@@ -38,48 +39,60 @@ import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class Keys {
|
||||
|
||||
@SqlUpdate("DELETE FROM keys WHERE number = :number")
|
||||
abstract void removeKeys(@Bind("number") String number);
|
||||
@SqlUpdate("DELETE FROM keys WHERE number = :number AND device_id = :device_id")
|
||||
abstract void removeKeys(@Bind("number") String number, @Bind("device_id") long deviceId);
|
||||
|
||||
@SqlUpdate("DELETE FROM keys WHERE id = :id")
|
||||
abstract void removeKey(@Bind("id") long id);
|
||||
|
||||
@SqlBatch("INSERT INTO keys (number, key_id, public_key, identity_key, last_resort) VALUES (:number, :key_id, :public_key, :identity_key, :last_resort)")
|
||||
@SqlBatch("INSERT INTO keys (number, device_id, key_id, public_key, identity_key, last_resort) VALUES " +
|
||||
"(:number, :device_id, :key_id, :public_key, :identity_key, :last_resort)")
|
||||
abstract void append(@PreKeyBinder List<PreKey> preKeys);
|
||||
|
||||
@SqlUpdate("INSERT INTO keys (number, key_id, public_key, identity_key, last_resort) VALUES (:number, :key_id, :public_key, :identity_key, :last_resort)")
|
||||
@SqlUpdate("INSERT INTO keys (number, device_id, key_id, public_key, identity_key, last_resort) VALUES " +
|
||||
"(:number, :device_id, :key_id, :public_key, :identity_key, :last_resort)")
|
||||
abstract void append(@PreKeyBinder PreKey preKey);
|
||||
|
||||
@SqlQuery("SELECT * FROM keys WHERE number = :number ORDER BY id LIMIT 1 FOR UPDATE")
|
||||
@SqlQuery("SELECT * FROM keys WHERE number = :number AND device_id = :device_id ORDER BY key_id ASC FOR UPDATE")
|
||||
@Mapper(PreKeyMapper.class)
|
||||
abstract PreKey retrieveFirst(@Bind("number") String number);
|
||||
abstract PreKey retrieveFirst(@Bind("number") String number, @Bind("device_id") long deviceId);
|
||||
|
||||
@Transaction(TransactionIsolationLevel.SERIALIZABLE)
|
||||
public void store(String number, PreKey lastResortKey, List<PreKey> keys) {
|
||||
public void store(String number, long deviceId, PreKey lastResortKey, List<PreKey> keys) {
|
||||
for (PreKey key : keys) {
|
||||
key.setNumber(number);
|
||||
key.setDeviceId(deviceId);
|
||||
}
|
||||
|
||||
lastResortKey.setNumber(number);
|
||||
lastResortKey.setDeviceId(deviceId);
|
||||
lastResortKey.setLastResort(true);
|
||||
|
||||
removeKeys(number);
|
||||
removeKeys(number, deviceId);
|
||||
append(keys);
|
||||
append(lastResortKey);
|
||||
}
|
||||
|
||||
@Transaction(TransactionIsolationLevel.SERIALIZABLE)
|
||||
public PreKey get(String number) {
|
||||
PreKey preKey = retrieveFirst(number);
|
||||
|
||||
if (preKey != null && !preKey.isLastResort()) {
|
||||
removeKey(preKey.getId());
|
||||
public UnstructuredPreKeyList get(String number, List<Account> accounts) {
|
||||
List<PreKey> preKeys = new LinkedList<>();
|
||||
for (Account account : accounts) {
|
||||
PreKey preKey = retrieveFirst(number, account.getDeviceId());
|
||||
if (preKey != null)
|
||||
preKeys.add(preKey);
|
||||
}
|
||||
|
||||
return preKey;
|
||||
for (PreKey preKey : preKeys) {
|
||||
if (!preKey.isLastResort())
|
||||
removeKey(preKey.getId());
|
||||
}
|
||||
|
||||
return new UnstructuredPreKeyList(preKeys);
|
||||
}
|
||||
|
||||
@BindingAnnotation(PreKeyBinder.PreKeyBinderFactory.class)
|
||||
@@ -95,6 +108,7 @@ public abstract class Keys {
|
||||
{
|
||||
sql.bind("id", preKey.getId());
|
||||
sql.bind("number", preKey.getNumber());
|
||||
sql.bind("device_id", preKey.getDeviceId());
|
||||
sql.bind("key_id", preKey.getKeyId());
|
||||
sql.bind("public_key", preKey.getPublicKey());
|
||||
sql.bind("identity_key", preKey.getIdentityKey());
|
||||
@@ -111,7 +125,7 @@ public abstract class Keys {
|
||||
public PreKey map(int i, ResultSet resultSet, StatementContext statementContext)
|
||||
throws SQLException
|
||||
{
|
||||
return new PreKey(resultSet.getLong("id"), resultSet.getString("number"),
|
||||
return new PreKey(resultSet.getLong("id"), resultSet.getString("number"), resultSet.getLong("device_id"),
|
||||
resultSet.getLong("key_id"), resultSet.getString("public_key"),
|
||||
resultSet.getString("identity_key"),
|
||||
resultSet.getInt("last_resort") == 1);
|
||||
|
||||
@@ -29,4 +29,6 @@ public interface PendingAccounts {
|
||||
@SqlQuery("SELECT verification_code FROM pending_accounts WHERE number = :number")
|
||||
String getCodeForNumber(@Bind("number") String number);
|
||||
|
||||
@SqlUpdate("DELETE FROM pending_accounts WHERE number = :number")
|
||||
void remove(@Bind("number") String number);
|
||||
}
|
||||
|
||||
@@ -41,6 +41,12 @@ public class PendingAccountsManager {
|
||||
pendingAccounts.insert(number, code);
|
||||
}
|
||||
|
||||
public void remove(String number) {
|
||||
if (memcachedClient != null)
|
||||
memcachedClient.delete(MEMCACHE_PREFIX + number);
|
||||
pendingAccounts.remove(number);
|
||||
}
|
||||
|
||||
public Optional<String> getCodeForNumber(String number) {
|
||||
String code = null;
|
||||
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* Copyright (C) 2013 Open WhisperSystems
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.whispersystems.textsecuregcm.storage;
|
||||
|
||||
import org.skife.jdbi.v2.sqlobject.Bind;
|
||||
import org.skife.jdbi.v2.sqlobject.SqlQuery;
|
||||
import org.skife.jdbi.v2.sqlobject.SqlUpdate;
|
||||
|
||||
public interface PendingDeviceRegistrations {
|
||||
|
||||
@SqlUpdate("WITH upsert AS (UPDATE pending_devices SET verification_code = :verification_code WHERE number = :number RETURNING *) " +
|
||||
"INSERT INTO pending_devices (number, verification_code) SELECT :number, :verification_code WHERE NOT EXISTS (SELECT * FROM upsert)")
|
||||
void insert(@Bind("number") String number, @Bind("verification_code") String verificationCode);
|
||||
|
||||
@SqlQuery("SELECT verification_code FROM pending_devices WHERE number = :number")
|
||||
String getCodeForNumber(@Bind("number") String number);
|
||||
|
||||
@SqlUpdate("DELETE FROM pending_devices WHERE number = :number")
|
||||
void remove(@Bind("number") String number);
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/**
|
||||
* Copyright (C) 2013 Open WhisperSystems
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.whispersystems.textsecuregcm.storage;
|
||||
|
||||
import com.google.common.base.Optional;
|
||||
import net.spy.memcached.MemcachedClient;
|
||||
|
||||
public class PendingDevicesManager {
|
||||
|
||||
private static final String MEMCACHE_PREFIX = "pending_devices";
|
||||
|
||||
private final PendingDeviceRegistrations pendingDevices;
|
||||
private final MemcachedClient memcachedClient;
|
||||
|
||||
public PendingDevicesManager(PendingDeviceRegistrations pendingDevices,
|
||||
MemcachedClient memcachedClient)
|
||||
{
|
||||
this.pendingDevices = pendingDevices;
|
||||
this.memcachedClient = memcachedClient;
|
||||
}
|
||||
|
||||
public void store(String number, String code) {
|
||||
if (memcachedClient != null) {
|
||||
memcachedClient.set(MEMCACHE_PREFIX + number, 0, code);
|
||||
}
|
||||
|
||||
pendingDevices.insert(number, code);
|
||||
}
|
||||
|
||||
public void remove(String number) {
|
||||
if (memcachedClient != null)
|
||||
memcachedClient.delete(MEMCACHE_PREFIX + number);
|
||||
pendingDevices.remove(number);
|
||||
}
|
||||
|
||||
public Optional<String> getCodeForNumber(String number) {
|
||||
String code = null;
|
||||
|
||||
if (memcachedClient != null) {
|
||||
code = (String)memcachedClient.get(MEMCACHE_PREFIX + number);
|
||||
}
|
||||
|
||||
if (code == null) {
|
||||
code = pendingDevices.getCodeForNumber(number);
|
||||
|
||||
if (code != null && memcachedClient != null) {
|
||||
memcachedClient.set(MEMCACHE_PREFIX + number, 0, code);
|
||||
}
|
||||
}
|
||||
|
||||
if (code != null) return Optional.of(code);
|
||||
else return Optional.absent();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* Copyright (C) 2013 Open WhisperSystems
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.whispersystems.textsecuregcm.storage;
|
||||
|
||||
import org.whispersystems.textsecuregcm.entities.EncryptedOutgoingMessage;
|
||||
|
||||
public class StoredMessageManager {
|
||||
StoredMessages storedMessages;
|
||||
public StoredMessageManager(StoredMessages storedMessages) {
|
||||
this.storedMessages = storedMessages;
|
||||
}
|
||||
|
||||
public void storeMessage(Account account, EncryptedOutgoingMessage outgoingMessage) {
|
||||
storedMessages.insert(account.getId(), outgoingMessage);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* Copyright (C) 2013 Open WhisperSystems
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.whispersystems.textsecuregcm.storage;
|
||||
|
||||
import org.skife.jdbi.v2.sqlobject.Bind;
|
||||
import org.skife.jdbi.v2.sqlobject.SqlQuery;
|
||||
import org.skife.jdbi.v2.sqlobject.SqlUpdate;
|
||||
import org.whispersystems.textsecuregcm.entities.EncryptedOutgoingMessage;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface StoredMessages {
|
||||
|
||||
@SqlUpdate("INSERT INTO stored_messages (destination_id, encrypted_message) VALUES :destination_id, :encrypted_message")
|
||||
void insert(@Bind("destination_id") long destinationAccountId, @Bind("encrypted_message") EncryptedOutgoingMessage encryptedOutgoingMessage);
|
||||
|
||||
@SqlQuery("SELECT encrypted_message FROM stored_messages WHERE destination_id = :account_id")
|
||||
List<EncryptedOutgoingMessage> getMessagesForAccountId(@Bind("account_id") long accountId);
|
||||
}
|
||||
Reference in New Issue
Block a user