Break out into a multi-module project

This commit is contained in:
Moxie Marlinspike
2019-04-20 21:56:20 -07:00
parent b41dde777e
commit d0d375aeb7
318 changed files with 255 additions and 215 deletions

View File

@@ -0,0 +1 @@
org.whispersystems.textsecuregcm.metrics.LoggingNetworkAppenderFactory

View File

@@ -0,0 +1 @@
org.whispersystems.textsecuregcm.metrics.JsonMetricsReporterFactory

View File

@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-2.0.xsd">
<changeSet id="1" author="moxie">
<createTable tableName="abusive_host_rules">
<column name="id" type="bigint" autoIncrement="true">
<constraints primaryKey="true" nullable="false"/>
</column>
<column name="host" type="inet">
<constraints nullable="false" unique="true"/>
</column>
<column name="blocked" type="tinyint">
<constraints nullable="false"/>
</column>
<column name="regions" type="text"/>
</createTable>
<createIndex tableName="abusive_host_rules" indexName="host_index">
<column name="host"/>
</createIndex>
</changeSet>
<changeSet id="2" author="moxie">
<addColumn tableName="abusive_host_rules">
<column name="notes" type="text"/>
</addColumn>
</changeSet>
</databaseChangeLog>

View File

@@ -0,0 +1,189 @@
<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-2.0.xsd"
logicalFilePath="migrations.xml">
<changeSet id="1" author="moxie">
<createTable tableName="accounts">
<column name="id" type="bigint" autoIncrement="true">
<constraints primaryKey="true" nullable="false"/>
</column>
<column name="number" type="varchar(255)">
<constraints unique="true" nullable="false"/>
</column>
<column name="auth_token" type="varchar(255)">
<constraints nullable="false"/>
</column>
<column name="salt" type="varchar(255)">
<constraints nullable="false"/>
</column>
<column name="signaling_key" type="varchar(255)"/>
<column name="gcm_id" type="text"/>
<column name="apn_id" type="text"/>
<column name="supports_sms" type="smallint" defaultValue="0"/>
</createTable>
<createTable tableName="pending_accounts">
<column name="id" type="bigint" autoIncrement="true">
<constraints primaryKey="true" nullable="false"/>
</column>
<column name="number" type="varchar(255)">
<constraints unique="true" nullable="false"/>
</column>
<column name="verification_code" type="varchar(255)">
<constraints nullable="false"/>
</column>
</createTable>
<createTable tableName="keys">
<column name="id" type="bigint" autoIncrement="true">
<constraints primaryKey="true" nullable="false"/>
</column>
<column name="number" type="varchar(255)">
<constraints nullable="false"/>
</column>
<column name="key_id" type="bigint">
<constraints nullable="false"/>
</column>
<column name="public_key" type="text">
<constraints nullable="false"/>
</column>
<column name="identity_key" type="text">
<constraints nullable="false"/>
</column>
<column name="last_resort" type="smallint" defaultValue="0"/>
</createTable>
<createIndex tableName="keys" indexName="keys_number_index">
<column name="number"/>
</createIndex>
</changeSet>
<changeSet id="2" author="matt">
<addColumn tableName="accounts">
<column name="data" type="json" />
</addColumn>
<sql>CREATE type device_t AS (id integer, "authToken" varchar(255), salt varchar(255), "signalingKey" varchar(255), "gcmId" text, "apnId" text);</sql>
<sql>CREATE type account_t AS (number varchar(255), "supportsSms" smallint, devices device_t array);</sql>
<sql>UPDATE accounts SET data = row_to_json(row(number, supports_sms, array[row(1, auth_token, salt, signaling_key, gcm_id, apn_id)::device_t])::account_t)</sql>
<addNotNullConstraint tableName="accounts" columnName="data"/>
<dropColumn tableName="accounts" columnName="auth_token"/>
<dropColumn tableName="accounts" columnName="salt"/>
<dropColumn tableName="accounts" columnName="signaling_key"/>
<dropColumn tableName="accounts" columnName="gcm_id"/>
<dropColumn tableName="accounts" columnName="apn_id"/>
<dropColumn tableName="accounts" columnName="supports_sms"/>
<sql>DROP type account_t;</sql>
<sql>DROP type device_t;</sql>
<addColumn tableName="keys">
<column name="device_id" type="bigint" defaultValue="1">
<constraints nullable="false" />
</column>
</addColumn>
<createTable tableName="pending_devices">
<column name="id" type="bigint" autoIncrement="true">
<constraints primaryKey="true" nullable="false"/>
</column>
<column name="number" type="text">
<constraints unique="true" nullable="false"/>
</column>
<column name="verification_code" type="text">
<constraints nullable="false"/>
</column>
</createTable>
<createTable tableName="messages">
<column name="id" type="bigint" autoIncrement="true">
<constraints primaryKey="true" nullable="false"/>
</column>
<column name="account_id" type="bigint">
<constraints nullable="false"/>
</column>
<column name="device_id" type="bigint">
<constraints nullable="false"/>
</column>
<column name="encrypted_message" type="text">
<constraints nullable="false"/>
</column>
</createTable>
<createIndex tableName="messages" indexName="messages_account_and_device">
<column name="account_id"/>
<column name="device_id"/>
</createIndex>
</changeSet>
<changeSet id="3" author="moxie">
<sql>CREATE OR REPLACE FUNCTION "custom_json_object_set_key"(
"json" json,
"key_to_set" TEXT,
"value_to_set" anyelement
)
RETURNS json
LANGUAGE sql
IMMUTABLE
STRICT
AS $function$
SELECT COALESCE(
(SELECT ('{' || string_agg(to_json("key") || ':' || "value", ',') || '}')
FROM (SELECT *
FROM json_each("json")
WHERE "key" &lt;&gt; "key_to_set"
UNION ALL
SELECT "key_to_set", to_json("value_to_set")) AS "fields"),
'{}'
)::json
$function$;</sql>
<sql>UPDATE accounts SET data = custom_json_object_set_key(data, 'identityKey', k.identity_key) FROM keys k WHERE (data->>'identityKey')::text is null AND k.number = data->>'number' AND k.last_resort = 1;</sql>
<sql>UPDATE accounts SET data = custom_json_object_set_key(data, 'identityKey', k.identity_key) FROM keys k WHERE (data->>'identityKey')::text is null AND k.number = data->>'number';</sql>
</changeSet>
<changeSet id="4" author="moxie">
<dropColumn tableName="keys" columnName="identity_key"/>
</changeSet>
<changeSet id="5" author="moxie">
<addColumn tableName="pending_accounts">
<column name="timestamp" type="bigint" defaultValueComputed="extract(epoch from now()) * 1000">
<constraints nullable="false"/>
</column>
</addColumn>
<addColumn tableName="pending_devices">
<column name="timestamp" type="bigint" defaultValueComputed="extract(epoch from now()) * 1000">
<constraints nullable="false"/>
</column>
</addColumn>
</changeSet>
</databaseChangeLog>

View File

@@ -0,0 +1,8 @@
_ _ _ _ _____
| | | | | (_) / ___|
| | | | |__ _ ___ _ __ ___ _ __\ `--. ___ _ ____ _____ _ __
| |/\| | '_ \| / __| '_ \ / _ \ '__|`--. \/ _ \ '__\ \ / / _ \ '__|
\ /\ / | | | \__ \ |_) | __/ | /\__/ / __/ | \ V / __/ |
\/ \/|_| |_|_|___/ .__/ \___|_| \____/ \___|_| \_/ \___|_|
| |
|_|

View File

@@ -0,0 +1,8 @@
-- keys: lock_key
-- argv: lock_value
if redis.call("GET", KEYS[1]) == ARGV[1] then
return redis.call("DEL", KEYS[1])
else
return 0
end

View File

@@ -0,0 +1,70 @@
-- keys: pending (KEYS[1])
-- argv: max_time (ARGV[1]), limit (ARGV[2])
local hgetall = function (key)
local bulk = redis.call('HGETALL', key)
local result = {}
local nextkey
for i, v in ipairs(bulk) do
if i % 2 == 1 then
nextkey = v
else
result[nextkey] = v
end
end
return result
end
local getNextInterval = function(interval)
if interval < 20000 then
return 20000
end
if interval < 40000 then
return 40000
end
if interval < 80000 then
return 80000
end
if interval < 160000 then
return 160000
end
if interval < 600000 then
return 600000
end
if interval < 1800000 then
return 1800000
end
return 3600000
end
local results = redis.call("ZRANGEBYSCORE", KEYS[1], 0, ARGV[1], "LIMIT", 0, ARGV[2])
local collated = {}
if results and next(results) then
for i, name in ipairs(results) do
local pending = hgetall(name)
local lastInterval = pending["interval"]
if lastInterval == nil then
lastInterval = 0
end
local nextInterval = getNextInterval(tonumber(lastInterval))
redis.call("HSET", name, "interval", nextInterval)
redis.call("ZADD", KEYS[1], tonumber(ARGV[1]) + nextInterval, name)
collated[i] = pending["account"] .. ":" .. pending["device"]
end
end
return collated

View File

@@ -0,0 +1,9 @@
-- keys: pending (KEYS[1]), user (KEYS[2])
-- args: timestamp (ARGV[1]), interval (ARGV[2]), account (ARGV[3]), device (ARGV[4])
redis.call("HSET", KEYS[2], "created", ARGV[1])
redis.call("HSET", KEYS[2], "interval", ARGV[2])
redis.call("HSET", KEYS[2], "account", ARGV[3])
redis.call("HSET", KEYS[2], "device", ARGV[4])
redis.call("ZADD", KEYS[1], ARGV[1], KEYS[2])

View File

@@ -0,0 +1,4 @@
-- keys: queue KEYS[1], endpoint (KEYS[2])
redis.call("DEL", KEYS[2])
return redis.call("ZREM", KEYS[1], KEYS[2])

View File

@@ -0,0 +1,10 @@
-- keys: queue_key, queue_locked_key
-- argv: limit
local locked = redis.call("GET", KEYS[2])
if locked then
return {}
end
return redis.call("ZRANGE", KEYS[1], 0, ARGV[1], "WITHSCORES")

View File

@@ -0,0 +1,10 @@
-- keys: queue_total_index
-- argv: max_time, limit
local results = redis.call("ZRANGEBYSCORE", KEYS[1], 0, ARGV[1], "LIMIT", 0, ARGV[2])
if results and next(results) then
redis.call("ZREM", KEYS[1], unpack(results))
end
return results

View File

@@ -0,0 +1,23 @@
-- keys: queue_key [1], queue_metadata_key [2], queue_total_index [3]
-- argv: message [1], current_time [2], sender (possibly null) [3], guid [4]
local messageId = redis.call("HINCRBY", KEYS[2], "counter", 1)
redis.call("ZADD", KEYS[1], "NX", messageId, ARGV[1])
if ARGV[3] ~= "nil" then
redis.call("HSET", KEYS[2], ARGV[3], messageId)
end
redis.call("HSET", KEYS[2], ARGV[4], messageId)
if ARGV[3] ~= "nil" then
redis.call("HSET", KEYS[2], messageId, ARGV[3])
end
redis.call("HSET", KEYS[2], messageId .. "guid", ARGV[4])
redis.call("EXPIRE", KEYS[1], 7776000)
redis.call("EXPIRE", KEYS[2], 7776000)
redis.call("ZADD", KEYS[3], "NX", ARGV[2], KEYS[1])
return messageId

View File

@@ -0,0 +1,28 @@
-- keys: queue_key, queue_metadata_key, queue_index
-- argv: guid_to_remove
local messageId = redis.call("HGET", KEYS[2], ARGV[1])
if messageId then
local envelope = redis.call("ZRANGEBYSCORE", KEYS[1], messageId, messageId, "LIMIT", 0, 1)
local sender = redis.call("HGET", KEYS[2], messageId)
redis.call("ZREMRANGEBYSCORE", KEYS[1], messageId, messageId)
redis.call("HDEL", KEYS[2], ARGV[1])
redis.call("HDEL", KEYS[2], messageId .. "guid")
if sender then
redis.call("HDEL", KEYS[2], sender)
redis.call("HDEL", KEYS[2], messageId)
end
if (redis.call("ZCARD", KEYS[1]) == 0) then
redis.call("ZREM", KEYS[3], KEYS[1])
end
if envelope and next(envelope) then
return envelope[1]
end
end
return nil

View File

@@ -0,0 +1,22 @@
-- keys: queue_key, queue_metadata_key, queue_index
-- argv: index_to_remove
local removedCount = redis.call("ZREMRANGEBYSCORE", KEYS[1], ARGV[1], ARGV[1])
local senderIndex = redis.call("HGET", KEYS[2], ARGV[1])
local guidIndex = redis.call("HGET", KEYS[2], ARGV[1] .. "guid")
if senderIndex then
redis.call("HDEL", KEYS[2], senderIndex)
redis.call("HDEL", KEYS[2], ARGV[1])
end
if guidIndex then
redis.call("HDEL", KEYS[2], guidIndex)
redis.call("HDEL", KEYS[2], ARGV[1] .. "guid")
end
if (redis.call("ZCARD", KEYS[1]) == 0) then
redis.call("ZREM", KEYS[3], KEYS[1])
end
return removedCount > 0

View File

@@ -0,0 +1,28 @@
-- keys: queue_key, queue_metadata_key, queue_index
-- argv: sender_to_remove
local messageId = redis.call("HGET", KEYS[2], ARGV[1])
if messageId then
local envelope = redis.call("ZRANGEBYSCORE", KEYS[1], messageId, messageId, "LIMIT", 0, 1)
local guid = redis.call("HGET", KEYS[2], messageId .. "guid")
redis.call("ZREMRANGEBYSCORE", KEYS[1], messageId, messageId)
redis.call("HDEL", KEYS[2], ARGV[1])
redis.call("HDEL", KEYS[2], messageId)
if guid then
redis.call("HDEL", KEYS[2], guid)
redis.call("HDEL", KEYS[2], messageId .. "guid")
end
if (redis.call("ZCARD", KEYS[1]) == 0) then
redis.call("ZREM", KEYS[3], KEYS[1])
end
if envelope and next(envelope) then
return envelope[1]
end
end
return nil

View File

@@ -0,0 +1,5 @@
-- keys: queue_key, queue_metadata_key, queue_index
redis.call("DEL", KEYS[1])
redis.call("DEL", KEYS[2])
redis.call("ZREM", KEYS[3], KEYS[1])

View File

@@ -0,0 +1,124 @@
<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-2.0.xsd">
<changeSet id="1" author="moxie">
<createTable tableName="messages">
<column name="id" type="bigint" autoIncrement="true">
<constraints primaryKey="true" nullable="false"/>
</column>
<column name="type" type="tinyint">
<constraints nullable="false"/>
</column>
<column name="relay" type="text">
<constraints nullable="false"/>
</column>
<column name="timestamp" type="bigint">
<constraints nullable="false"/>
</column>
<column name="source" type="text">
<constraints nullable="false"/>
</column>
<column name="source_device" type="int">
<constraints nullable="false"/>
</column>
<column name="destination" type="text">
<constraints nullable="false"/>
</column>
<column name="destination_device" type="int">
<constraints nullable="false"/>
</column>
<column name="message" type="bytea">
<constraints nullable="false"/>
</column>
</createTable>
<createIndex tableName="messages" indexName="destination_index">
<column name="destination"></column>
<column name="destination_device"></column>
</createIndex>
<createIndex tableName="messages" indexName="destination_and_type_index">
<column name="destination"></column>
<column name="destination_device"></column>
<column name="type"></column>
</createIndex>
</changeSet>
<changeSet id="2" author="moxie">
<addColumn tableName="messages">
<column name="content" type="bytea"/>
</addColumn>
<dropNotNullConstraint tableName="messages" columnName="message"/>
</changeSet>
<changeSet id="3" author="moxie">
<sql>CREATE RULE bounded_message_queue AS ON INSERT TO messages DO ALSO DELETE FROM messages WHERE id IN (SELECT id FROM messages WHERE destination = NEW.destination AND destination_device = NEW.destination_device ORDER BY timestamp DESC OFFSET 5000);</sql>
</changeSet>
<changeSet id="4" author="moxie">
<sql>DROP RULE bounded_message_queue ON messages;</sql>
<sql>CREATE RULE bounded_message_queue AS ON INSERT TO messages DO ALSO DELETE FROM messages WHERE id IN (SELECT id FROM messages WHERE destination = NEW.destination AND destination_device = NEW.destination_device ORDER BY timestamp DESC OFFSET 1000);</sql>
</changeSet>
<changeSet id="5" author="moxie">
<addColumn tableName="messages">
<column name="deleted" type="integer"/>
</addColumn>
<sql>DROP RULE bounded_message_queue ON messages;</sql>
</changeSet>
<changeSet id="6" author="moxie">
<sql>CREATE RULE bounded_message_queue AS ON INSERT TO messages DO ALSO DELETE FROM messages WHERE id IN (SELECT id FROM messages WHERE destination = NEW.destination AND destination_device = NEW.destination_device ORDER BY timestamp DESC OFFSET 1000);</sql>
</changeSet>
<changeSet id="7" author="moxie">
<dropColumn tableName="messages" columnName="deleted"/>
<sql>DROP RULE bounded_message_queue ON messages;</sql>
</changeSet>
<changeSet id="8" author="moxie">
<sql>CREATE RULE bounded_message_queue AS ON INSERT TO messages DO ALSO DELETE FROM messages WHERE id IN (SELECT id FROM messages WHERE destination = NEW.destination AND destination_device = NEW.destination_device ORDER BY timestamp DESC OFFSET 1000);</sql>
</changeSet>
<changeSet id="9" author="moxie">
<sql>DROP RULE bounded_message_queue ON messages;</sql>
</changeSet>
<changeSet id="10" author="moxie">
<sql>CREATE RULE bounded_message_queue AS ON INSERT TO messages DO ALSO DELETE FROM messages WHERE id IN (SELECT id FROM messages WHERE destination = NEW.destination AND destination_device = NEW.destination_device ORDER BY timestamp DESC OFFSET 1000);</sql>
</changeSet>
<changeSet id="11" author="moxie">
<addColumn tableName="messages">
<column name="guid" type="uuid"/>
</addColumn>
<addColumn tableName="messages">
<column name="server_timestamp" type="bigint"/>
</addColumn>
<dropNotNullConstraint tableName="messages" columnName="source"/>
<dropNotNullConstraint tableName="messages" columnName="source_device"/>
</changeSet>
<changeSet id="12" author="moxie" runInTransaction="false">
<sql>CREATE INDEX CONCURRENTLY guid_index ON messages (guid);</sql>
</changeSet>
</databaseChangeLog>