Compare commits
356 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
af4397d00e | ||
|
|
87cd46d42f | ||
|
|
05e38de854 | ||
|
|
4d57da79ac | ||
|
|
8309273cdb | ||
|
|
394467b694 | ||
|
|
251fa186dc | ||
|
|
45fab90a00 | ||
|
|
5d2ff7fd3b | ||
|
|
f7b0357563 | ||
|
|
75dabfcd86 | ||
|
|
fa0efcaebb | ||
|
|
7c2d482afe | ||
|
|
624e45ec8f | ||
|
|
8d0a494bee | ||
|
|
cb0dae7ae1 | ||
|
|
53c520c1ef | ||
|
|
662ceda87b | ||
|
|
cbe51139f9 | ||
|
|
252666b020 | ||
|
|
2d78b484a4 | ||
|
|
1155ad62ba | ||
|
|
8239f0ec89 | ||
|
|
89036535c7 | ||
|
|
aadf492a17 | ||
|
|
49ac4472c4 | ||
|
|
6fa4d87ec2 | ||
|
|
229d1bd89c | ||
|
|
6bde704cc1 | ||
|
|
ddc0649111 | ||
|
|
de80d7a3c1 | ||
|
|
19ca8bf94d | ||
|
|
c4008500da | ||
|
|
b6b543d09b | ||
|
|
ca98e0c45a | ||
|
|
b672a344e3 | ||
|
|
29c4694842 | ||
|
|
22e449dc81 | ||
|
|
020af68951 | ||
|
|
8939188dac | ||
|
|
8ec6cd5ae0 | ||
|
|
7373d1da32 | ||
|
|
847ecdb24f | ||
|
|
f5af61304c | ||
|
|
4891ef94e2 | ||
|
|
fb865d9c6c | ||
|
|
00750c1c41 | ||
|
|
11bd3ce19c | ||
|
|
de8d93bfb4 | ||
|
|
ae190638d8 | ||
|
|
afaa10b176 | ||
|
|
d42e45b43c | ||
|
|
4d464a762a | ||
|
|
ffd1c80885 | ||
|
|
bfbfe93af0 | ||
|
|
f6f38e2fbd | ||
|
|
2473ac8e62 | ||
|
|
30e65782a2 | ||
|
|
4c9a1be489 | ||
|
|
d53f1dc247 | ||
|
|
772688cc9a | ||
|
|
813e390a6e | ||
|
|
358795ca77 | ||
|
|
5d145d020f | ||
|
|
63db4a6770 | ||
|
|
62d91a3f25 | ||
|
|
dbfaa1187d | ||
|
|
9e131743c5 | ||
|
|
2d3de3a988 | ||
|
|
bec414bd5c | ||
|
|
8b5f8bf427 | ||
|
|
69a14de876 | ||
|
|
0f5256b758 | ||
|
|
1fa8ece0b0 | ||
|
|
f3256f8c29 | ||
|
|
2dba844a59 | ||
|
|
66d53cf6f0 | ||
|
|
2eab162f8c | ||
|
|
0feab732e3 | ||
|
|
7e7e3a5c25 | ||
|
|
61aeb30a8a | ||
|
|
50901473db | ||
|
|
b95564ccc4 | ||
|
|
633755e3e5 | ||
|
|
55485ed6f6 | ||
|
|
898e9de371 | ||
|
|
52399585a6 | ||
|
|
666850cc2b | ||
|
|
45987d101b | ||
|
|
131af1a147 | ||
|
|
4db5e34305 | ||
|
|
e1bc6c18d2 | ||
|
|
4250869835 | ||
|
|
4e2fcfce5f | ||
|
|
edfc25832b | ||
|
|
bbdcb82e86 | ||
|
|
253ea3a5ba | ||
|
|
a618c44f4b | ||
|
|
2407391557 | ||
|
|
3e1d117b68 | ||
|
|
6f7bbe3d77 | ||
|
|
0c1e7d842c | ||
|
|
c92559c7cf | ||
|
|
b13b94bc99 | ||
|
|
138316ab33 | ||
|
|
9e603380fb | ||
|
|
85d2ea9eef | ||
|
|
1d63384de9 | ||
|
|
c7d628ee62 | ||
|
|
6662ce651b | ||
|
|
9366e19014 | ||
|
|
7af2433167 | ||
|
|
6fa6b64d9c | ||
|
|
3da371107d | ||
|
|
d953f5cde3 | ||
|
|
36d86e83cf | ||
|
|
238f896837 | ||
|
|
605dcb95db | ||
|
|
7ac354918d | ||
|
|
6bdda194ff | ||
|
|
837c97a15a | ||
|
|
c03b35cc5a | ||
|
|
c3213b6d39 | ||
|
|
eeec613888 | ||
|
|
ff83f19ee9 | ||
|
|
17f72be3ea | ||
|
|
f0ba2c9804 | ||
|
|
e27c12b3a0 | ||
|
|
fbe5a0babb | ||
|
|
d75cf059c0 | ||
|
|
06b039dac4 | ||
|
|
c99691fca5 | ||
|
|
baffa0703d | ||
|
|
78e76338c8 | ||
|
|
d7e42ae922 | ||
|
|
420e1d089c | ||
|
|
1bdbe67a54 | ||
|
|
39dda7c410 | ||
|
|
18d1069418 | ||
|
|
e7c54b3225 | ||
|
|
9f89e84b58 | ||
|
|
68b7d824ab | ||
|
|
e75e38271b | ||
|
|
1834229177 | ||
|
|
52bbdbd953 | ||
|
|
309dd31165 | ||
|
|
96ab308e6c | ||
|
|
b2b4e20817 | ||
|
|
d9d91ed53e | ||
|
|
bad3a266fb | ||
|
|
eb97cb07c0 | ||
|
|
1dd2050c47 | ||
|
|
578e9b6501 | ||
|
|
48ca510fb2 | ||
|
|
4ecb26203c | ||
|
|
465e8b8449 | ||
|
|
d9a89c26f9 | ||
|
|
99a12d3e76 | ||
|
|
1e07ef8daa | ||
|
|
6f5fc324ce | ||
|
|
749a7a43b4 | ||
|
|
0bf04a27fa | ||
|
|
1ee2032a38 | ||
|
|
25ebf298a0 | ||
|
|
da2727bd42 | ||
|
|
ee8936c96a | ||
|
|
4f2018d416 | ||
|
|
bd29d93293 | ||
|
|
f1c599a166 | ||
|
|
f4d35aefb7 | ||
|
|
135363068f | ||
|
|
8e8bc2b827 | ||
|
|
f916e61533 | ||
|
|
b05f40c33d | ||
|
|
05ec2d3479 | ||
|
|
0bd1ace8b5 | ||
|
|
267aabb361 | ||
|
|
e83703ee63 | ||
|
|
22d3fe0c5a | ||
|
|
b6b719e8b9 | ||
|
|
ec3ca97994 | ||
|
|
e37ab06fa3 | ||
|
|
e522f30140 | ||
|
|
1b710afb49 | ||
|
|
ca80ce3806 | ||
|
|
6ffb8264a2 | ||
|
|
aaf5f4250b | ||
|
|
f4a49617f3 | ||
|
|
c08d412edb | ||
|
|
6ea5aa0494 | ||
|
|
61ee38f3f2 | ||
|
|
09065420ed | ||
|
|
6a39482216 | ||
|
|
640a47bafc | ||
|
|
c02e8928df | ||
|
|
834fdb5088 | ||
|
|
4c99d4c328 | ||
|
|
3f89143650 | ||
|
|
652cfd16ab | ||
|
|
63afc00660 | ||
|
|
3eb49ad7b6 | ||
|
|
6911de3e7a | ||
|
|
673c3bff0e | ||
|
|
afd4b8a87b | ||
|
|
37e7fcf699 | ||
|
|
23a95d617b | ||
|
|
bfeac37ef4 | ||
|
|
dc2195d8a9 | ||
|
|
760ca33abc | ||
|
|
9debf45ed4 | ||
|
|
92d07a3446 | ||
|
|
11213d0e50 | ||
|
|
a69b382190 | ||
|
|
1f43ed5d8d | ||
|
|
191477645d | ||
|
|
b3190b2b8a | ||
|
|
2818e5f3ef | ||
|
|
6696213bbd | ||
|
|
9fe35de833 | ||
|
|
8f2ad5972d | ||
|
|
7aec08439a | ||
|
|
ca326c934f | ||
|
|
254d5b286a | ||
|
|
ad2bb0abb1 | ||
|
|
a36d86d0d7 | ||
|
|
165141acaf | ||
|
|
56e0ffbc24 | ||
|
|
0a41391984 | ||
|
|
1f2619cbd8 | ||
|
|
0d0d2daee0 | ||
|
|
3166b79356 | ||
|
|
72e1c95ebf | ||
|
|
aa886b6dfc | ||
|
|
9cad167f85 | ||
|
|
b6f78a3549 | ||
|
|
3b792a4496 | ||
|
|
03e5a58c75 | ||
|
|
a80f44b833 | ||
|
|
37bc1086b5 | ||
|
|
8ff089ec91 | ||
|
|
a1da27dea8 | ||
|
|
ff4f9f77f2 | ||
|
|
8eaf4b97b9 | ||
|
|
835a90813a | ||
|
|
a26bdb0666 | ||
|
|
1a25b65d0d | ||
|
|
bc87852873 | ||
|
|
44af128526 | ||
|
|
ab8b1a45e3 | ||
|
|
c0111255d7 | ||
|
|
335164df8b | ||
|
|
488e55ab2c | ||
|
|
d92d8d5e0f | ||
|
|
5d97bc9d20 | ||
|
|
8347ca3201 | ||
|
|
34033e7cec | ||
|
|
214bc53fae | ||
|
|
6ce63945c4 | ||
|
|
31c862b230 | ||
|
|
64c5638706 | ||
|
|
53dd49a25b | ||
|
|
6cb93fc4f9 | ||
|
|
ff90f7d9f6 | ||
|
|
3d5f737b53 | ||
|
|
029634df06 | ||
|
|
79f47536f1 | ||
|
|
27a57e0259 | ||
|
|
fdca7ea9f1 | ||
|
|
8f2f872113 | ||
|
|
46d3d5d4d8 | ||
|
|
5717ea3b5d | ||
|
|
809a9f99f1 | ||
|
|
67c780482b | ||
|
|
df4263a7ae | ||
|
|
624cf453bc | ||
|
|
18bd04b502 | ||
|
|
eaad560404 | ||
|
|
07daec5e7e | ||
|
|
24dc9509fe | ||
|
|
cd18291036 | ||
|
|
073c172b4c | ||
|
|
cbc1296898 | ||
|
|
2828ce0f6c | ||
|
|
bd5c8cb450 | ||
|
|
eb05f2cad4 | ||
|
|
37bdfa2332 | ||
|
|
49f7d8b712 | ||
|
|
5ce8a1f59a | ||
|
|
61c36b7f4d | ||
|
|
2fb13541b0 | ||
|
|
4b3c6de61b | ||
|
|
5fe3f04ba9 | ||
|
|
d6098f2c9c | ||
|
|
fa021ff3e5 | ||
|
|
8f2cd859e1 | ||
|
|
9df8b4ef19 | ||
|
|
17c8a79cf7 | ||
|
|
74211590b5 | ||
|
|
27f46c8bf8 | ||
|
|
e109ff7397 | ||
|
|
daa0ef3352 | ||
|
|
a10365e30d | ||
|
|
6f1c94c84f | ||
|
|
c06286b0f4 | ||
|
|
00367022a7 | ||
|
|
1281831d1c | ||
|
|
d17f5906d2 | ||
|
|
d04124a42e | ||
|
|
c149d1e962 | ||
|
|
9cb5bfcf2a | ||
|
|
d709cc4ed3 | ||
|
|
a95fafde1a | ||
|
|
16fe53a281 | ||
|
|
3c7632a9dc | ||
|
|
ab6b6ab09d | ||
|
|
652e3b1e06 | ||
|
|
4eb3cc9d97 | ||
|
|
6e7a06d7be | ||
|
|
4215ef6aa9 | ||
|
|
cff83d8124 | ||
|
|
1467fce54c | ||
|
|
9be7bacb18 | ||
|
|
b76b4ffcc4 | ||
|
|
687e3bcb18 | ||
|
|
41b02d4e90 | ||
|
|
19854af8d8 | ||
|
|
ca7b63743e | ||
|
|
9d5cdf11ae | ||
|
|
d4a24f152a | ||
|
|
e7c24f1f01 | ||
|
|
c40c1fafc9 | ||
|
|
431d6d6f59 | ||
|
|
2e67c13fe3 | ||
|
|
87bcacdeb4 | ||
|
|
728757fbdd | ||
|
|
9404dfb89e | ||
|
|
075b268805 | ||
|
|
632941602b | ||
|
|
4ea1d13d26 | ||
|
|
d3f68e7d52 | ||
|
|
5e1ae88b74 | ||
|
|
a36f20558f | ||
|
|
926503fa3f | ||
|
|
5d0e6ff464 | ||
|
|
c81cbe9550 | ||
|
|
4e26676290 | ||
|
|
5159a594c3 | ||
|
|
d130ec5785 | ||
|
|
252b9f6f8b | ||
|
|
50dfd34754 | ||
|
|
a1a9db6357 | ||
|
|
a75fa16527 | ||
|
|
a67e5ef83b | ||
|
|
f4ba415202 | ||
|
|
98e7efa9a0 | ||
|
|
7dd2e1184b |
1396
.editorconfig
1396
.editorconfig
File diff suppressed because it is too large
Load Diff
4
.github/FUNDING.yml
vendored
4
.github/FUNDING.yml
vendored
@ -1,4 +0,0 @@
|
||||
# Copyright 2021 Signal Messenger, LLC
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
custom: https://signal.org/donate/
|
||||
0
.github/stale.yml
vendored
0
.github/stale.yml
vendored
23
.github/workflows/test.yml
vendored
23
.github/workflows/test.yml
vendored
@ -1,23 +0,0 @@
|
||||
name: Service CI
|
||||
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
container: ubuntu:22.04
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # v3.1.0
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@de1bb2b0c5634f0fc4438d7aa9944e68f9bf86cc # v3.6.0
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: 17
|
||||
cache: 'maven'
|
||||
env:
|
||||
# work around an issue with actions/runner setting an incorrect HOME in containers, which breaks maven caching
|
||||
# https://github.com/actions/setup-java/issues/356
|
||||
HOME: /root
|
||||
- name: Build with Maven
|
||||
run: ./mvnw -e -B verify
|
||||
31
.gitignore
vendored
31
.gitignore
vendored
@ -1,31 +0,0 @@
|
||||
target
|
||||
local.properties
|
||||
.idea
|
||||
*.iml
|
||||
run.sh
|
||||
*~
|
||||
local.yml
|
||||
config/production.yml
|
||||
config/federated.yml
|
||||
config/staging.yml
|
||||
config/testing.yml
|
||||
config/deploy.properties
|
||||
/service/config/production.yml
|
||||
/service/config/federated.yml
|
||||
/service/config/staging.yml
|
||||
/service/config/testing.yml
|
||||
/service/config/deploy.properties
|
||||
/service/dependency-reduced-pom.xml
|
||||
.java-version
|
||||
.opsmanage
|
||||
put.sh
|
||||
deployer-staging.properties
|
||||
deployer-production.properties
|
||||
deployer.log
|
||||
/service/src/main/resources/org/signal/badges/Badges_*.properties
|
||||
!/service/src/main/resources/org/signal/badges/Badges_en.properties
|
||||
/service/src/main/resources/org/signal/subscriptions/Subscriptions_*.properties
|
||||
!/service/src/main/resources/org/signal/subscriptions/Subscriptions_en.properties
|
||||
.project
|
||||
.classpath
|
||||
.settings
|
||||
11
.gitmodules
vendored
11
.gitmodules
vendored
@ -1,11 +0,0 @@
|
||||
# Note that the implementation of the spam filter is private; internal
|
||||
# developers will need to override this URL with:
|
||||
#
|
||||
# ```
|
||||
# git config submodule.spam-filter.url PRIVATE_URL
|
||||
# ```
|
||||
#
|
||||
# External developers may safely ignore this submodule.
|
||||
[submodule "spam-filter"]
|
||||
path = spam-filter
|
||||
url = REDACTED
|
||||
@ -1,9 +0,0 @@
|
||||
<extensions xmlns="http://maven.apache.org/EXTENSIONS/1.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/EXTENSIONS/1.0.0 http://maven.apache.org/xsd/core-extensions-1.0.0.xsd">
|
||||
<extension>
|
||||
<groupId>fr.brouillard.oss</groupId>
|
||||
<artifactId>jgitver-maven-plugin</artifactId>
|
||||
<version>1.7.1</version>
|
||||
</extension>
|
||||
</extensions>
|
||||
@ -1,14 +0,0 @@
|
||||
<configuration xmlns="http://jgitver.github.io/maven/configuration/1.1.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://jgitver.github.io/maven/configuration/1.1.0 https://jgitver.github.io/maven/configuration/jgitver-configuration-v1_1_0.xsd">
|
||||
<useDirty>true</useDirty>
|
||||
<useDefaultBranchingPolicy>false</useDefaultBranchingPolicy>
|
||||
<branchPolicies>
|
||||
<branchPolicy>
|
||||
<pattern>(.*)</pattern>
|
||||
<transformations>
|
||||
<transformation>IGNORE</transformation>
|
||||
</transformations>
|
||||
</branchPolicy>
|
||||
</branchPolicies>
|
||||
</configuration>
|
||||
BIN
.mvn/wrapper/maven-wrapper.jar
vendored
BIN
.mvn/wrapper/maven-wrapper.jar
vendored
Binary file not shown.
18
.mvn/wrapper/maven-wrapper.properties
vendored
18
.mvn/wrapper/maven-wrapper.properties
vendored
@ -1,18 +0,0 @@
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.6/apache-maven-3.8.6-bin.zip
|
||||
wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar
|
||||
26
README.md
26
README.md
@ -1,26 +0,0 @@
|
||||
Signal-Server
|
||||
=================
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
|
||||
Looking for protocol documentation? Check out the website!
|
||||
|
||||
https://signal.org/docs/
|
||||
|
||||
Cryptography Notice
|
||||
------------
|
||||
|
||||
This distribution includes cryptographic software. The country in which you currently reside may have restrictions on the import, possession, use, and/or re-export to another country, of encryption software.
|
||||
BEFORE using any encryption software, please check your country's laws, regulations and policies concerning the import, possession, or use, and re-export of encryption software, to see if this is permitted.
|
||||
See <https://www.wassenaar.org/> for more information.
|
||||
|
||||
The U.S. Government Department of Commerce, Bureau of Industry and Security (BIS), has classified this software as Export Commodity Control Number (ECCN) 5D002.C.1, which includes information security software using or performing cryptographic functions with asymmetric algorithms.
|
||||
The form and manner of this distribution makes it eligible for export under the License Exception ENC Technology Software Unrestricted (TSU) exception (see the BIS Export Administration Regulations, Section 740.13) for both object code and source code.
|
||||
|
||||
License
|
||||
---------------------
|
||||
|
||||
Copyright 2013-2022 Signal Messenger, LLC
|
||||
|
||||
Licensed under the AGPLv3: https://www.gnu.org/licenses/agpl-3.0.html
|
||||
@ -1,86 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ Copyright 2022 Signal Messenger, LLC
|
||||
~ SPDX-License-Identifier: AGPL-3.0-only
|
||||
-->
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>TextSecureServer</artifactId>
|
||||
<groupId>org.whispersystems.textsecure</groupId>
|
||||
<version>JGITVER</version>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>event-logger</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.google.cloud</groupId>
|
||||
<artifactId>google-cloud-logging</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-stdlib</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.jetbrains</groupId>
|
||||
<!--
|
||||
depends on an outdated version (13.0) for JDK 6 compatibility, but it’s safe to override
|
||||
https://youtrack.jetbrains.com/issue/KT-25047
|
||||
-->
|
||||
<artifactId>annotations</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlinx</groupId>
|
||||
<artifactId>kotlinx-serialization-json</artifactId>
|
||||
<version>${kotlinx-serialization.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<sourceDirectory>${project.basedir}/src/main/kotlin</sourceDirectory>
|
||||
<testSourceDirectory>${project.basedir}/src/test/kotlin</testSourceDirectory>
|
||||
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-maven-plugin</artifactId>
|
||||
<version>${kotlin.version}</version>
|
||||
|
||||
<executions>
|
||||
<execution>
|
||||
<id>compile</id>
|
||||
<goals>
|
||||
<goal>compile</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
|
||||
<execution>
|
||||
<id>test-compile</id>
|
||||
<goals>
|
||||
<goal>test-compile</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<compilerPlugins>
|
||||
<plugin>kotlinx-serialization</plugin>
|
||||
</compilerPlugins>
|
||||
</configuration>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-maven-serialization</artifactId>
|
||||
<version>${kotlin.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
@ -1,40 +0,0 @@
|
||||
/*
|
||||
* Copyright 2022 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.signal.event
|
||||
|
||||
import java.util.Collections
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.modules.SerializersModule
|
||||
import kotlinx.serialization.modules.polymorphic
|
||||
import kotlinx.serialization.modules.subclass
|
||||
|
||||
val module = SerializersModule {
|
||||
polymorphic(Event::class) {
|
||||
subclass(RemoteConfigSetEvent::class)
|
||||
subclass(RemoteConfigDeleteEvent::class)
|
||||
}
|
||||
}
|
||||
val jsonFormat = Json { serializersModule = module }
|
||||
|
||||
sealed interface Event
|
||||
|
||||
@Serializable
|
||||
data class RemoteConfigSetEvent(
|
||||
val token: String,
|
||||
val name: String,
|
||||
val percentage: Int,
|
||||
val defaultValue: String? = null,
|
||||
val value: String? = null,
|
||||
val hashKey: String? = null,
|
||||
val uuids: Collection<String> = Collections.emptyList(),
|
||||
) : Event
|
||||
|
||||
@Serializable
|
||||
data class RemoteConfigDeleteEvent(
|
||||
val token: String,
|
||||
val name: String,
|
||||
) : Event
|
||||
@ -1,41 +0,0 @@
|
||||
/*
|
||||
* Copyright 2022 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.signal.event
|
||||
|
||||
import com.google.cloud.logging.LogEntry
|
||||
import com.google.cloud.logging.Logging
|
||||
import com.google.cloud.logging.MonitoredResourceUtil
|
||||
import com.google.cloud.logging.Payload.JsonPayload
|
||||
import com.google.cloud.logging.Severity
|
||||
import com.google.protobuf.Struct
|
||||
import com.google.protobuf.util.JsonFormat
|
||||
import kotlinx.serialization.encodeToString
|
||||
|
||||
interface AdminEventLogger {
|
||||
fun logEvent(event: Event, labels: Map<String, String>?)
|
||||
fun logEvent(event: Event) = logEvent(event, null)
|
||||
}
|
||||
|
||||
class NoOpAdminEventLogger : AdminEventLogger {
|
||||
override fun logEvent(event: Event, labels: Map<String, String>?) {}
|
||||
}
|
||||
|
||||
class GoogleCloudAdminEventLogger(private val logging: Logging, private val projectId: String, private val logName: String) : AdminEventLogger {
|
||||
override fun logEvent(event: Event, labels: Map<String, String>?) {
|
||||
val structBuilder = Struct.newBuilder()
|
||||
JsonFormat.parser().merge(jsonFormat.encodeToString(event), structBuilder)
|
||||
val struct = structBuilder.build()
|
||||
|
||||
val logEntryBuilder = LogEntry.newBuilder(JsonPayload.of(struct))
|
||||
.setLogName(logName)
|
||||
.setSeverity(Severity.NOTICE)
|
||||
.setResource(MonitoredResourceUtil.getResource(projectId, "project"));
|
||||
if (labels != null) {
|
||||
logEntryBuilder.setLabels(labels);
|
||||
}
|
||||
logging.write(listOf(logEntryBuilder.build()))
|
||||
}
|
||||
}
|
||||
@ -1,22 +0,0 @@
|
||||
/*
|
||||
* Copyright 2023 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.signal.event
|
||||
|
||||
import com.google.cloud.logging.Logging
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.mockito.Mockito.mock
|
||||
|
||||
class GoogleCloudAdminEventLoggerTest {
|
||||
|
||||
@Test
|
||||
fun logEvent() {
|
||||
val logging = mock(Logging::class.java)
|
||||
val logger = GoogleCloudAdminEventLogger(logging, "my-project", "test")
|
||||
|
||||
val event = RemoteConfigDeleteEvent("token", "test")
|
||||
logger.logEvent(event)
|
||||
}
|
||||
}
|
||||
BIN
favicon.ico
Normal file
BIN
favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 9.4 KiB |
291
grpc/index.html
Normal file
291
grpc/index.html
Normal file
@ -0,0 +1,291 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Protobuf module documentation</title>
|
||||
<link rel="stylesheet" href="static/mystyles.css" />
|
||||
<script src="static/fontawesome.js"></script>
|
||||
<script src="static/lunr.js"></script>
|
||||
<script src="static/theme.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<nav class="level pt-6 mb-5 pb-2 bottom-border">
|
||||
<div class="level-left">
|
||||
<p class="level-item has-text-centered is-size-3 has-text-weight-medium">
|
||||
<a class="link is-info" href="index.html">
|
||||
Protobuf module documentation
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="level-right">
|
||||
|
||||
<form action="search.html" class="mr-2 has-text-centered">
|
||||
<input type="text" name="query" class="input mr-3 navbar-search-query-input" placeholder="Search">
|
||||
<input type="submit" class="button is-primary mr-5" value="Search" />
|
||||
</form>
|
||||
|
||||
|
||||
<p class="level-item has-text-centered is-size-5">
|
||||
<a href="" class="link is-info mr-5" target="_blank">
|
||||
|
||||
<span>Source</span>
|
||||
</a>
|
||||
|
||||
<button class="theme-toggle" id="theme-toggle" title="Toggles light & dark" aria-label="auto" aria-live="polite" type="button">
|
||||
<svg class="sun-and-moon" aria-hidden="true" width="24" height="24" viewBox="0 0 24 24">
|
||||
<mask class="moon" id="moon-mask">
|
||||
<rect x="0" y="0" width="100%" height="100%" fill="white" />
|
||||
<circle cx="24" cy="10" r="6" fill="black" />
|
||||
</mask>
|
||||
<circle class="sun" cx="12" cy="12" r="6" mask="url(#moon-mask)" fill="currentColor" />
|
||||
<g class="sun-beams" stroke="currentColor">
|
||||
<line x1="12" y1="1" x2="12" y2="3" />
|
||||
<line x1="12" y1="21" x2="12" y2="23" />
|
||||
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64" />
|
||||
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78" />
|
||||
<line x1="1" y1="12" x2="3" y2="12" />
|
||||
<line x1="21" y1="12" x2="23" y2="12" />
|
||||
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36" />
|
||||
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22" />
|
||||
</g>
|
||||
</svg>
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<div id="main-content" class="container mb-6">
|
||||
|
||||
<p class="mb-2">
|
||||
<a style="font-size: 0.9em" href="#packages">↓ Packages</a>
|
||||
</p>
|
||||
<div class="block content"><h1>Chat gRPC API</h1>
|
||||
<h2>Request metadata</h2>
|
||||
<p>Clients may provide headers for gRPC requests via <a href="https://grpc.io/docs/guides/metadata/">gRPC metadata</a> which translates directly to HTTP/2 headers.</p>
|
||||
<ul>
|
||||
<li>Clients should provide a <code>User-Agent</code> header on all gRPC requests. </li>
|
||||
<li>Clients may provide an <code>Accept-Language</code> on any gRPC requests.</li>
|
||||
</ul>
|
||||
<h2>Authentication</h2>
|
||||
<p>To authenticate for a specific signal account + device, clients may provide a <a href="https://www.rfc-editor.org/info/rfc7617/">Basic authorization header</a>
|
||||
on the request. The username must be the account identifier encoded in the 8-4-4-4-12 format followed by a '.'
|
||||
character and then the device id serialized as a string.</p>
|
||||
<p>All services either require or forbid providing authentication via an Authorization header. If the service is annotated
|
||||
with the <code>require.auth</code> option <code>AUTH_ONLY_AUTHENTICATED</code>, all requests to the service must contain valid authentication
|
||||
headers. If the service is annotated with the option <code>AUTH_ONLY_ANONYMOUS</code> the client must not provide an authorization
|
||||
header. If provided, the request will fail with a <code>BAD_AUTHENTICATION</code> error.</p>
|
||||
<h2>Errors</h2>
|
||||
<p>In the gRPC protocol all errors are at the request level. That is, errors are returned in response to individual requests and do not impact other H2 streams on the same connection nor terminate the connection.</p>
|
||||
<p>For errors that may be returned by any RPCs, the chat server will return the well-defined gRPC status codes returned as part of every RPC call. For errors that are specific to a particular RPC, the error must be encoded in the service proto definition and will be returned with a <code>Status</code> of <code>OK</code>.</p>
|
||||
<p>Status errors include additional metadata as described in <a href="https://google.aip.dev/193#error_model">AIP-193 (google's richer error model)</a>. Every <code>Status != OK</code> response returned by the chat server's application layer will include a <code>Grpc-Status-Details-Bin</code> response trailer with a <code>google.rpc.Status</code> proto.</p>
|
||||
<p>Each <code>google.rpc.Status</code> must have a status matching the top-level status on the gRPC response. Additionally, a single <code>ErrorInfo</code> must always be present in the details field of the <code>Status</code>. The <code>ErrorInfo</code> must contain a <code>domain</code> field and a <code>reason</code> field.</p>
|
||||
<p>The <code>domain</code> for a status error generated by the chat server will always be <code>grpc.chat.signal.org</code></p>
|
||||
<p>The server may set the <code>reason</code> to match the enum string for the <code>Status.Code</code> if there is no need to further distinguish a code. Clients should inspect the <code>reason</code>, not the <code>status</code>, for automated error handling.</p>
|
||||
<p>The chat server may return the following errors from any RPC</p>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="text-align: left;">Status Code</th>
|
||||
<th style="text-align: left;">Reason</th>
|
||||
<th style="text-align: left;">Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="text-align: left;"><code>INVALID_ARGUMENT</code></td>
|
||||
<td style="text-align: left;"><code>UPGRADE_REQUIRED</code></td>
|
||||
<td style="text-align: left;">The client version provided in the <code>User-Agent</code> is no longer supported. The client must upgrade to use the service.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="text-align: left;"><code>INVALID_ARGUMENT</code></td>
|
||||
<td style="text-align: left;"><code>CONSTRAINT_VIOLATED</code></td>
|
||||
<td style="text-align: left;">The RPC argument violated a constraint that was annotated or documented in the service definition. It is always possible to check this constraint without communicating with the chat server. This always represents a client bug or out of date client. <br><br> The <code>details</code> may include a <a href="https://github.com/googleapis/googleapis/blob/8c06c1e04ae562f49f411357577c700e9142f33c/google/rpc/error_details.proto#L236"><code>BadRequest</code> message</a> that indicates additional information about the invalid field.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="text-align: left;"><code>INVALID_ARGUMENT</code></td>
|
||||
<td style="text-align: left;"><code>BAD_AUTHENTICATION</code></td>
|
||||
<td style="text-align: left;">The request has incorrectly set authentication credentials for the RPC. This represents a client bug where the authorization mode is not correct for the RPC. For example, <br><br> The RPC was for an anonymous service, but included an Authentication header in the RPC metadata. <br><br>The RPC should only be made by the primary device, but the request had linked device credentials.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="text-align: left;"><code>UNAUTHENTICATED</code></td>
|
||||
<td style="text-align: left;"><code>INVALID_CREDENTIALS</code></td>
|
||||
<td style="text-align: left;">The account credentials provided in the authorization header are not valid.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="text-align: left;"><code>RESOURCE_EXHAUSTED</code></td>
|
||||
<td style="text-align: left;"><code>RESOURCE_EXHAUSTED</code></td>
|
||||
<td style="text-align: left;">A server-side resource was exhausted. The <code>details</code> field may include a <a href="https://github.com/googleapis/googleapis/blob/8c06c1e04ae562f49f411357577c700e9142f33c/google/rpc/error_details.proto#L92"><code>RetryInfo</code> message</a> that includes the amount of time in seconds the client should wait before retrying the request. <br><br> If a <code>RetryInfo</code> is present, the client must wait the indicated time before retrying the request. If absent, the client should retry with an exponential backoff.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="text-align: left;"><code>ABORTED</code></td>
|
||||
<td style="text-align: left;"><code>STREAM_CLOSED</code></td>
|
||||
<td style="text-align: left;">The server terminated a streaming RPC for some domain specific reason. The <code>details</code> field must include a message with additional information about why the stream was closed. This message must be defined in the corresponding service proto for the streaming RPC. This status must only be returned from server-side streaming RPCs. Clients must handle a STREAM_CLOSED status even if no corresponding message is defined in the version of the proto they are targeting. Unless otherwise noted, if a stream termination message is defined, clients must handle the error at any point in the message stream.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="text-align: left;"><code>UNAVAILABLE</code></td>
|
||||
<td style="text-align: left;"><code>UNAVAILABLE</code></td>
|
||||
<td style="text-align: left;">There was an internal error processing the RPC. The client should retry the request with exponential backoff.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<h3>Logging Errors</h3>
|
||||
<p>When logging error responses, clients may always log the status code, domain, and reason.</p>
|
||||
<p>For errors with domain <code>grpc.chat.signal.org</code> and reason <code>CONSTRAINT_VIOLATED</code>, clients should check the <code>details</code> field for a <code>BadRequest</code> proto. If present, they should log the <code>field</code> of each violation in <code>field_violations</code>.</p>
|
||||
<h3>GOAWAY</h3>
|
||||
<p>In a partial or total server outage, the server may start rejecting new connection requests or terminating existing connections with an HTTP/2 GOAWAY frame with code 0x40.</p>
|
||||
<p>If clients receive a 0x40 GOAWAY they should retry connections with an aggressive exponential backoff.</p></div>
|
||||
|
||||
<h3 id="packages" class="title is-3">
|
||||
Packages
|
||||
</h3>
|
||||
<div class="block">This site contains the documentation for the following Protobuf packages.</div>
|
||||
<div class="block">
|
||||
|
||||
<h4 class="title is-5">
|
||||
|
||||
<a href="org.signal.chat.account.html">org.signal.chat.account</a>
|
||||
|
||||
</h4>
|
||||
|
||||
|
||||
<h4 class="title is-5">
|
||||
|
||||
<a href="org.signal.chat.attachments.html">org.signal.chat.attachments</a>
|
||||
|
||||
</h4>
|
||||
|
||||
|
||||
<h4 class="title is-5">
|
||||
|
||||
<a href="org.signal.chat.backup.html">org.signal.chat.backup</a>
|
||||
|
||||
</h4>
|
||||
|
||||
|
||||
<h4 class="title is-5">
|
||||
|
||||
<a href="org.signal.chat.calling.html">org.signal.chat.calling</a>
|
||||
|
||||
</h4>
|
||||
|
||||
|
||||
<h4 class="title is-5">
|
||||
|
||||
<a href="org.signal.chat.calling.quality.html">org.signal.chat.calling.quality</a>
|
||||
|
||||
</h4>
|
||||
|
||||
|
||||
<h4 class="title is-5">
|
||||
|
||||
<a href="org.signal.chat.challenge.html">org.signal.chat.challenge</a>
|
||||
|
||||
</h4>
|
||||
|
||||
|
||||
<h4 class="title is-5">
|
||||
|
||||
<a href="org.signal.chat.common.html">org.signal.chat.common</a>
|
||||
|
||||
</h4>
|
||||
|
||||
|
||||
<h4 class="title is-5">
|
||||
|
||||
<a href="org.signal.chat.credentials.html">org.signal.chat.credentials</a>
|
||||
|
||||
</h4>
|
||||
|
||||
|
||||
<h4 class="title is-5">
|
||||
|
||||
<a href="org.signal.chat.device.html">org.signal.chat.device</a>
|
||||
|
||||
</h4>
|
||||
|
||||
|
||||
<h4 class="title is-5">
|
||||
|
||||
<a href="org.signal.chat.donations.html">org.signal.chat.donations</a>
|
||||
|
||||
</h4>
|
||||
|
||||
|
||||
<h4 class="title is-5">
|
||||
|
||||
<a href="org.signal.chat.errors.html">org.signal.chat.errors</a>
|
||||
|
||||
</h4>
|
||||
|
||||
|
||||
<h4 class="title is-5">
|
||||
|
||||
<a href="org.signal.chat.keys.html">org.signal.chat.keys</a>
|
||||
|
||||
</h4>
|
||||
|
||||
|
||||
<h4 class="title is-5">
|
||||
|
||||
<a href="org.signal.chat.messages.html">org.signal.chat.messages</a>
|
||||
|
||||
</h4>
|
||||
|
||||
|
||||
<h4 class="title is-5">
|
||||
|
||||
<a href="org.signal.chat.one_time_donations.html">org.signal.chat.one_time_donations</a>
|
||||
|
||||
</h4>
|
||||
|
||||
|
||||
<h4 class="title is-5">
|
||||
|
||||
<a href="org.signal.chat.payments.html">org.signal.chat.payments</a>
|
||||
|
||||
</h4>
|
||||
|
||||
|
||||
<h4 class="title is-5">
|
||||
|
||||
<a href="org.signal.chat.profile.html">org.signal.chat.profile</a>
|
||||
|
||||
</h4>
|
||||
|
||||
|
||||
<h4 class="title is-5">
|
||||
|
||||
<a href="org.signal.chat.require.html">org.signal.chat.require</a>
|
||||
|
||||
</h4>
|
||||
|
||||
|
||||
<h4 class="title is-5">
|
||||
|
||||
<a href="org.signal.chat.subscriptions.html">org.signal.chat.subscriptions</a>
|
||||
|
||||
</h4>
|
||||
|
||||
|
||||
<h4 class="title is-5">
|
||||
|
||||
<a href="org.signal.chat.tag.html">org.signal.chat.tag</a>
|
||||
|
||||
</h4>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer class="footer">
|
||||
<div class="content has-text-centered">
|
||||
|
||||
<p>Built with <strong><a href="https://github.com/markvincze/sabledocs" target="_blank">sabledocs</a></strong>.</p>
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
2585
grpc/org.signal.chat.account.html
Normal file
2585
grpc/org.signal.chat.account.html
Normal file
File diff suppressed because it is too large
Load Diff
487
grpc/org.signal.chat.attachments.html
Normal file
487
grpc/org.signal.chat.attachments.html
Normal file
@ -0,0 +1,487 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Protobuf module documentation</title>
|
||||
<link rel="stylesheet" href="static/mystyles.css" />
|
||||
<script src="static/fontawesome.js"></script>
|
||||
<script src="static/lunr.js"></script>
|
||||
<script src="static/theme.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<nav class="level pt-6 mb-5 pb-2 bottom-border">
|
||||
<div class="level-left">
|
||||
<p class="level-item has-text-centered is-size-3 has-text-weight-medium">
|
||||
<a class="link is-info" href="index.html">
|
||||
Protobuf module documentation
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="level-right">
|
||||
|
||||
<form action="search.html" class="mr-2 has-text-centered">
|
||||
<input type="text" name="query" class="input mr-3 navbar-search-query-input" placeholder="Search">
|
||||
<input type="submit" class="button is-primary mr-5" value="Search" />
|
||||
</form>
|
||||
|
||||
|
||||
<p class="level-item has-text-centered is-size-5">
|
||||
<a href="" class="link is-info mr-5" target="_blank">
|
||||
|
||||
<span>Source</span>
|
||||
</a>
|
||||
|
||||
<button class="theme-toggle" id="theme-toggle" title="Toggles light & dark" aria-label="auto" aria-live="polite" type="button">
|
||||
<svg class="sun-and-moon" aria-hidden="true" width="24" height="24" viewBox="0 0 24 24">
|
||||
<mask class="moon" id="moon-mask">
|
||||
<rect x="0" y="0" width="100%" height="100%" fill="white" />
|
||||
<circle cx="24" cy="10" r="6" fill="black" />
|
||||
</mask>
|
||||
<circle class="sun" cx="12" cy="12" r="6" mask="url(#moon-mask)" fill="currentColor" />
|
||||
<g class="sun-beams" stroke="currentColor">
|
||||
<line x1="12" y1="1" x2="12" y2="3" />
|
||||
<line x1="12" y1="21" x2="12" y2="23" />
|
||||
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64" />
|
||||
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78" />
|
||||
<line x1="1" y1="12" x2="3" y2="12" />
|
||||
<line x1="21" y1="12" x2="23" y2="12" />
|
||||
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36" />
|
||||
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22" />
|
||||
</g>
|
||||
</svg>
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<div id="main-content" class="container mb-6">
|
||||
<p class="mb-2">
|
||||
<a style="font-size: 0.9em" href="index.html">← Back to packages</a>
|
||||
</p>
|
||||
<h3 class="title is-3">
|
||||
|
||||
Package <code>org.signal.chat.attachments</code>
|
||||
|
||||
</h3>
|
||||
|
||||
<div class="block">
|
||||
|
||||
<p>
|
||||
<a href="#service-org.signal.chat.attachments.Attachments">
|
||||
<span class="tag" style="min-width: 6em">Service</span>
|
||||
<code>Attachments</code>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
|
||||
<p>
|
||||
<a href="#message-org.signal.chat.attachments.GetStickerUploadFormRequest">
|
||||
<span class="tag" style="min-width: 6em">Message</span>
|
||||
<code>GetStickerUploadFormRequest</code>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
<p>
|
||||
<a href="#message-org.signal.chat.attachments.GetStickerUploadFormResponse">
|
||||
<span class="tag" style="min-width: 6em">Message</span>
|
||||
<code>GetStickerUploadFormResponse</code>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
<p>
|
||||
<a href="#message-org.signal.chat.attachments.GetUploadFormRequest">
|
||||
<span class="tag" style="min-width: 6em">Message</span>
|
||||
<code>GetUploadFormRequest</code>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
<p>
|
||||
<a href="#message-org.signal.chat.attachments.GetUploadFormResponse">
|
||||
<span class="tag" style="min-width: 6em">Message</span>
|
||||
<code>GetUploadFormResponse</code>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<div class="block">
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="block">
|
||||
<h3 class="title is-3">
|
||||
Services
|
||||
</h3>
|
||||
|
||||
|
||||
<div>
|
||||
|
||||
|
||||
<h4 class="title is-4 type-heading" id="service-org.signal.chat.attachments.Attachments">
|
||||
<a href="#service-org.signal.chat.attachments.Attachments" title="org.signal.chat.attachments.Attachments">service Attachments</a>
|
||||
|
||||
</h4>
|
||||
|
||||
<div class="block content">
|
||||
<p></p>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="box">
|
||||
<div class="block">
|
||||
<h5 class="title is-5" id="service-org.signal.chat.attachments.Attachments-GetUploadForm">
|
||||
<a href="#service-org.signal.chat.attachments.Attachments-GetUploadForm">
|
||||
rpc <code>GetUploadForm</code>
|
||||
</a>
|
||||
</h5>
|
||||
Request: <code><a href="org.signal.chat.attachments.html#message-org.signal.chat.attachments.GetUploadFormRequest"><span title="org.signal.chat.attachments.GetUploadFormRequest">org.signal.chat.attachments.GetUploadFormRequest</span>
|
||||
</a></code><br>
|
||||
Response: <code><a href="org.signal.chat.attachments.html#message-org.signal.chat.attachments.GetUploadFormResponse"><span title="org.signal.chat.attachments.GetUploadFormResponse">org.signal.chat.attachments.GetUploadFormResponse</span>
|
||||
</a></code><br>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="block">
|
||||
<p><p>Retrieve an upload form that can be used to perform a resumable upload</p></p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="box">
|
||||
<div class="block">
|
||||
<h5 class="title is-5" id="service-org.signal.chat.attachments.Attachments-GetStickerUploadForm">
|
||||
<a href="#service-org.signal.chat.attachments.Attachments-GetStickerUploadForm">
|
||||
rpc <code>GetStickerUploadForm</code>
|
||||
</a>
|
||||
</h5>
|
||||
Request: <code><a href="org.signal.chat.attachments.html#message-org.signal.chat.attachments.GetStickerUploadFormRequest"><span title="org.signal.chat.attachments.GetStickerUploadFormRequest">org.signal.chat.attachments.GetStickerUploadFormRequest</span>
|
||||
</a></code><br>
|
||||
Response: <code><a href="org.signal.chat.attachments.html#message-org.signal.chat.attachments.GetStickerUploadFormResponse"><span title="org.signal.chat.attachments.GetStickerUploadFormResponse">org.signal.chat.attachments.GetStickerUploadFormResponse</span>
|
||||
</a></code><br>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="block">
|
||||
<p><p>Retrieve an upload form that can be used to upload a sticker pack</p></p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h3 class="title is-3">
|
||||
Messages
|
||||
</h3>
|
||||
|
||||
|
||||
|
||||
<div class="block">
|
||||
|
||||
|
||||
<h4 class="title is-4 type-heading" id="message-org.signal.chat.attachments.GetStickerUploadFormRequest">
|
||||
<span>
|
||||
<a href="#message-org.signal.chat.attachments.GetStickerUploadFormRequest" title="org.signal.chat.attachments.GetStickerUploadFormRequest">message GetStickerUploadFormRequest</a>
|
||||
|
||||
</span>
|
||||
|
||||
</h4>
|
||||
|
||||
<div class="block content">
|
||||
|
||||
</div>
|
||||
|
||||
<table class="table is-fullwidth is-hoverable is-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>Field</th>
|
||||
<th>Type</th>
|
||||
<th>Description</th>
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
<tr>
|
||||
<td>1</td>
|
||||
<td>
|
||||
sticker_count
|
||||
</td>
|
||||
<td>
|
||||
<code title="uint32">
|
||||
|
||||
<span>uint32</span>
|
||||
</code>
|
||||
</td>
|
||||
<td class="content"><p>The number of stickers in the sticker pack to upload</p></td>
|
||||
|
||||
</tr>
|
||||
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="block">
|
||||
|
||||
|
||||
<h4 class="title is-4 type-heading" id="message-org.signal.chat.attachments.GetStickerUploadFormResponse">
|
||||
<span>
|
||||
<a href="#message-org.signal.chat.attachments.GetStickerUploadFormResponse" title="org.signal.chat.attachments.GetStickerUploadFormResponse">message GetStickerUploadFormResponse</a>
|
||||
|
||||
</span>
|
||||
|
||||
</h4>
|
||||
|
||||
<div class="block content">
|
||||
|
||||
</div>
|
||||
|
||||
<table class="table is-fullwidth is-hoverable is-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>Field</th>
|
||||
<th>Type</th>
|
||||
<th>Description</th>
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
<tr>
|
||||
<td>1</td>
|
||||
<td>
|
||||
pack_id
|
||||
</td>
|
||||
<td>
|
||||
<code title="string">
|
||||
|
||||
<span>string</span>
|
||||
</code>
|
||||
</td>
|
||||
<td class="content"><p>A randomly-generated ID for the new sticker pack</p></td>
|
||||
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>2</td>
|
||||
<td>
|
||||
manifest_upload_form
|
||||
</td>
|
||||
<td>
|
||||
<code title="org.signal.chat.common.S3UploadForm">
|
||||
|
||||
<a href="org.signal.chat.common.html#message-org.signal.chat.common.S3UploadForm"><span title="org.signal.chat.common.S3UploadForm">org.signal.chat.common.S3UploadForm</span>
|
||||
</a>
|
||||
</code>
|
||||
</td>
|
||||
<td class="content"><p>An upload form clients must use to upload a manifest for the sticker pack</p></td>
|
||||
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>3</td>
|
||||
<td>
|
||||
sticker_upload_forms
|
||||
</td>
|
||||
<td>
|
||||
<code title="org.signal.chat.common.S3UploadForm">
|
||||
|
||||
repeated
|
||||
|
||||
<a href="org.signal.chat.common.html#message-org.signal.chat.common.S3UploadForm"><span title="org.signal.chat.common.S3UploadForm">org.signal.chat.common.S3UploadForm</span>
|
||||
</a>
|
||||
</code>
|
||||
</td>
|
||||
<td class="content"><p>Upload forms for individual stickers within the sticker pack</p></td>
|
||||
|
||||
</tr>
|
||||
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="block">
|
||||
|
||||
|
||||
<h4 class="title is-4 type-heading" id="message-org.signal.chat.attachments.GetUploadFormRequest">
|
||||
<span>
|
||||
<a href="#message-org.signal.chat.attachments.GetUploadFormRequest" title="org.signal.chat.attachments.GetUploadFormRequest">message GetUploadFormRequest</a>
|
||||
|
||||
</span>
|
||||
|
||||
</h4>
|
||||
|
||||
<div class="block content">
|
||||
|
||||
</div>
|
||||
|
||||
<table class="table is-fullwidth is-hoverable is-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>Field</th>
|
||||
<th>Type</th>
|
||||
<th>Description</th>
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
<tr>
|
||||
<td>1</td>
|
||||
<td>
|
||||
uploadLength
|
||||
</td>
|
||||
<td>
|
||||
<code title="uint64">
|
||||
|
||||
<span>uint64</span>
|
||||
</code>
|
||||
</td>
|
||||
<td class="content"><p>The length of the attachment for the requested upload form. Uploads
|
||||
performed with this form will be limited to the provided length.</p></td>
|
||||
|
||||
</tr>
|
||||
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="block">
|
||||
|
||||
|
||||
<h4 class="title is-4 type-heading" id="message-org.signal.chat.attachments.GetUploadFormResponse">
|
||||
<span>
|
||||
<a href="#message-org.signal.chat.attachments.GetUploadFormResponse" title="org.signal.chat.attachments.GetUploadFormResponse">message GetUploadFormResponse</a>
|
||||
|
||||
</span>
|
||||
|
||||
</h4>
|
||||
|
||||
<div class="block content">
|
||||
|
||||
</div>
|
||||
|
||||
<table class="table is-fullwidth is-hoverable is-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>Field</th>
|
||||
<th>Type</th>
|
||||
<th>Description</th>
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
|
||||
|
||||
|
||||
<tr class="nohover"><td style="border: 0px"></td></tr>
|
||||
<tr class="nohover">
|
||||
<td class="oneof-heading" colspan="4">
|
||||
<div class="oneof-heading">
|
||||
<strong>oneof outcome</strong>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
1
|
||||
</td>
|
||||
<td>
|
||||
upload_form
|
||||
</td>
|
||||
<td>
|
||||
<code title="org.signal.chat.common.UploadForm">
|
||||
|
||||
<a href="org.signal.chat.common.html#message-org.signal.chat.common.UploadForm"><span title="org.signal.chat.common.UploadForm">org.signal.chat.common.UploadForm</span>
|
||||
</a>
|
||||
</code>
|
||||
</td>
|
||||
<td class="content"></td>
|
||||
|
||||
</tr>
|
||||
|
||||
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
2
|
||||
</td>
|
||||
<td>
|
||||
exceeds_max_upload_length
|
||||
</td>
|
||||
<td>
|
||||
<code title="org.signal.chat.errors.FailedPrecondition">
|
||||
|
||||
<a href="org.signal.chat.errors.html#message-org.signal.chat.errors.FailedPrecondition"><span title="org.signal.chat.errors.FailedPrecondition">org.signal.chat.errors.FailedPrecondition</span>
|
||||
</a>
|
||||
</code>
|
||||
</td>
|
||||
<td class="content"><p>The request size was larger than the maximum supported upload size. The
|
||||
maximum upload size is subject to change and is governed by
|
||||
<code>global.attachments.maxBytes</code></p></td>
|
||||
|
||||
</tr>
|
||||
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<footer class="footer">
|
||||
<div class="content has-text-centered">
|
||||
|
||||
<p>Built with <strong><a href="https://github.com/markvincze/sabledocs" target="_blank">sabledocs</a></strong>.</p>
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
4119
grpc/org.signal.chat.backup.html
Normal file
4119
grpc/org.signal.chat.backup.html
Normal file
File diff suppressed because it is too large
Load Diff
544
grpc/org.signal.chat.calling.html
Normal file
544
grpc/org.signal.chat.calling.html
Normal file
@ -0,0 +1,544 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Protobuf module documentation</title>
|
||||
<link rel="stylesheet" href="static/mystyles.css" />
|
||||
<script src="static/fontawesome.js"></script>
|
||||
<script src="static/lunr.js"></script>
|
||||
<script src="static/theme.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<nav class="level pt-6 mb-5 pb-2 bottom-border">
|
||||
<div class="level-left">
|
||||
<p class="level-item has-text-centered is-size-3 has-text-weight-medium">
|
||||
<a class="link is-info" href="index.html">
|
||||
Protobuf module documentation
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="level-right">
|
||||
|
||||
<form action="search.html" class="mr-2 has-text-centered">
|
||||
<input type="text" name="query" class="input mr-3 navbar-search-query-input" placeholder="Search">
|
||||
<input type="submit" class="button is-primary mr-5" value="Search" />
|
||||
</form>
|
||||
|
||||
|
||||
<p class="level-item has-text-centered is-size-5">
|
||||
<a href="" class="link is-info mr-5" target="_blank">
|
||||
|
||||
<span>Source</span>
|
||||
</a>
|
||||
|
||||
<button class="theme-toggle" id="theme-toggle" title="Toggles light & dark" aria-label="auto" aria-live="polite" type="button">
|
||||
<svg class="sun-and-moon" aria-hidden="true" width="24" height="24" viewBox="0 0 24 24">
|
||||
<mask class="moon" id="moon-mask">
|
||||
<rect x="0" y="0" width="100%" height="100%" fill="white" />
|
||||
<circle cx="24" cy="10" r="6" fill="black" />
|
||||
</mask>
|
||||
<circle class="sun" cx="12" cy="12" r="6" mask="url(#moon-mask)" fill="currentColor" />
|
||||
<g class="sun-beams" stroke="currentColor">
|
||||
<line x1="12" y1="1" x2="12" y2="3" />
|
||||
<line x1="12" y1="21" x2="12" y2="23" />
|
||||
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64" />
|
||||
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78" />
|
||||
<line x1="1" y1="12" x2="3" y2="12" />
|
||||
<line x1="21" y1="12" x2="23" y2="12" />
|
||||
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36" />
|
||||
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22" />
|
||||
</g>
|
||||
</svg>
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<div id="main-content" class="container mb-6">
|
||||
<p class="mb-2">
|
||||
<a style="font-size: 0.9em" href="index.html">← Back to packages</a>
|
||||
</p>
|
||||
<h3 class="title is-3">
|
||||
|
||||
Package <code>org.signal.chat.calling</code>
|
||||
|
||||
</h3>
|
||||
|
||||
<div class="block">
|
||||
|
||||
<p>
|
||||
<a href="#service-org.signal.chat.calling.Calling">
|
||||
<span class="tag" style="min-width: 6em">Service</span>
|
||||
<code>Calling</code>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
|
||||
<p>
|
||||
<a href="#message-org.signal.chat.calling.GetCallingRelaysRequest">
|
||||
<span class="tag" style="min-width: 6em">Message</span>
|
||||
<code>GetCallingRelaysRequest</code>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
<p>
|
||||
<a href="#message-org.signal.chat.calling.GetCallingRelaysResponse">
|
||||
<span class="tag" style="min-width: 6em">Message</span>
|
||||
<code>GetCallingRelaysResponse</code>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
<p>
|
||||
<a href="#message-org.signal.chat.calling.GetCallingRelaysResponse.HostnameUrlList">
|
||||
<span class="tag" style="min-width: 6em">Message</span>
|
||||
<code>HostnameUrlList</code>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
<p>
|
||||
<a href="#message-org.signal.chat.calling.GetCallingRelaysResponse.IpUrlList">
|
||||
<span class="tag" style="min-width: 6em">Message</span>
|
||||
<code>IpUrlList</code>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
<p>
|
||||
<a href="#message-org.signal.chat.calling.GetCallingRelaysResponse.Relay">
|
||||
<span class="tag" style="min-width: 6em">Message</span>
|
||||
<code>Relay</code>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<div class="block">
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="block">
|
||||
<h3 class="title is-3">
|
||||
Services
|
||||
</h3>
|
||||
|
||||
|
||||
<div>
|
||||
|
||||
|
||||
<h4 class="title is-4 type-heading" id="service-org.signal.chat.calling.Calling">
|
||||
<a href="#service-org.signal.chat.calling.Calling" title="org.signal.chat.calling.Calling">service Calling</a>
|
||||
|
||||
</h4>
|
||||
|
||||
<div class="block content">
|
||||
<p><p>Provides methods for getting credentials and relay options for one-on-one
|
||||
calls.</p></p>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="box">
|
||||
<div class="block">
|
||||
<h5 class="title is-5" id="service-org.signal.chat.calling.Calling-GetCallingRelays">
|
||||
<a href="#service-org.signal.chat.calling.Calling-GetCallingRelays">
|
||||
rpc <code>GetCallingRelays</code>
|
||||
</a>
|
||||
</h5>
|
||||
Request: <code><a href="org.signal.chat.calling.html#message-org.signal.chat.calling.GetCallingRelaysRequest"><span title="org.signal.chat.calling.GetCallingRelaysRequest">org.signal.chat.calling.GetCallingRelaysRequest</span>
|
||||
</a></code><br>
|
||||
Response: <code><a href="org.signal.chat.calling.html#message-org.signal.chat.calling.GetCallingRelaysResponse"><span title="org.signal.chat.calling.GetCallingRelaysResponse">org.signal.chat.calling.GetCallingRelaysResponse</span>
|
||||
</a></code><br>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="block">
|
||||
<p><p>Retrieves TURN credentials and relay options for one-on-one calls.</p></p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h3 class="title is-3">
|
||||
Messages
|
||||
</h3>
|
||||
|
||||
|
||||
|
||||
<div class="block">
|
||||
|
||||
|
||||
<h4 class="title is-4 type-heading" id="message-org.signal.chat.calling.GetCallingRelaysRequest">
|
||||
<span>
|
||||
<a href="#message-org.signal.chat.calling.GetCallingRelaysRequest" title="org.signal.chat.calling.GetCallingRelaysRequest">message GetCallingRelaysRequest</a>
|
||||
|
||||
</span>
|
||||
|
||||
</h4>
|
||||
|
||||
<div class="block content">
|
||||
|
||||
</div>
|
||||
|
||||
<table class="table is-fullwidth is-hoverable is-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>Field</th>
|
||||
<th>Type</th>
|
||||
<th>Description</th>
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="block">
|
||||
|
||||
|
||||
<h4 class="title is-4 type-heading" id="message-org.signal.chat.calling.GetCallingRelaysResponse">
|
||||
<span>
|
||||
<a href="#message-org.signal.chat.calling.GetCallingRelaysResponse" title="org.signal.chat.calling.GetCallingRelaysResponse">message GetCallingRelaysResponse</a>
|
||||
|
||||
</span>
|
||||
|
||||
</h4>
|
||||
|
||||
<div class="block content">
|
||||
|
||||
</div>
|
||||
|
||||
<table class="table is-fullwidth is-hoverable is-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>Field</th>
|
||||
<th>Type</th>
|
||||
<th>Description</th>
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
<tr>
|
||||
<td>1</td>
|
||||
<td>
|
||||
relays
|
||||
</td>
|
||||
<td>
|
||||
<code title="org.signal.chat.calling.GetCallingRelaysResponse.Relay">
|
||||
|
||||
repeated
|
||||
|
||||
<a href="org.signal.chat.calling.html#message-org.signal.chat.calling.GetCallingRelaysResponse.Relay"><span title="org.signal.chat.calling.GetCallingRelaysResponse.Relay">org.signal.chat.calling.GetCallingRelaysResponse.Relay</span>
|
||||
</a>
|
||||
</code>
|
||||
</td>
|
||||
<td class="content"><p>A collection of calling relays a client may use for one-on-one calls.</p></td>
|
||||
|
||||
</tr>
|
||||
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="block">
|
||||
|
||||
|
||||
<h4 class="title is-4 type-heading" id="message-org.signal.chat.calling.GetCallingRelaysResponse.HostnameUrlList">
|
||||
<span>
|
||||
<a href="#message-org.signal.chat.calling.GetCallingRelaysResponse.HostnameUrlList" title="org.signal.chat.calling.GetCallingRelaysResponse.HostnameUrlList">message HostnameUrlList</a>
|
||||
|
||||
<br /><span style="font-size: 0.7em; font-weight: normal">(Nested in <a href="org.signal.chat.calling.html#message-org.signal.chat.calling.GetCallingRelaysResponse"><span title="org.signal.chat.calling.GetCallingRelaysResponse">org.signal.chat.calling.GetCallingRelaysResponse</span>
|
||||
</a>)</span>
|
||||
|
||||
</span>
|
||||
|
||||
</h4>
|
||||
|
||||
<div class="block content">
|
||||
|
||||
</div>
|
||||
|
||||
<table class="table is-fullwidth is-hoverable is-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>Field</th>
|
||||
<th>Type</th>
|
||||
<th>Description</th>
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
<tr>
|
||||
<td>1</td>
|
||||
<td>
|
||||
urls
|
||||
</td>
|
||||
<td>
|
||||
<code title="string">
|
||||
|
||||
repeated
|
||||
|
||||
<span>string</span>
|
||||
</code>
|
||||
</td>
|
||||
<td class="content"><p>A collection of hostname-based TURN, TURNS, or STUN URLs a client can use
|
||||
to connect to a relay.</p></td>
|
||||
|
||||
</tr>
|
||||
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="block">
|
||||
|
||||
|
||||
<h4 class="title is-4 type-heading" id="message-org.signal.chat.calling.GetCallingRelaysResponse.IpUrlList">
|
||||
<span>
|
||||
<a href="#message-org.signal.chat.calling.GetCallingRelaysResponse.IpUrlList" title="org.signal.chat.calling.GetCallingRelaysResponse.IpUrlList">message IpUrlList</a>
|
||||
|
||||
<br /><span style="font-size: 0.7em; font-weight: normal">(Nested in <a href="org.signal.chat.calling.html#message-org.signal.chat.calling.GetCallingRelaysResponse"><span title="org.signal.chat.calling.GetCallingRelaysResponse">org.signal.chat.calling.GetCallingRelaysResponse</span>
|
||||
</a>)</span>
|
||||
|
||||
</span>
|
||||
|
||||
</h4>
|
||||
|
||||
<div class="block content">
|
||||
|
||||
</div>
|
||||
|
||||
<table class="table is-fullwidth is-hoverable is-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>Field</th>
|
||||
<th>Type</th>
|
||||
<th>Description</th>
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
<tr>
|
||||
<td>1</td>
|
||||
<td>
|
||||
urls
|
||||
</td>
|
||||
<td>
|
||||
<code title="string">
|
||||
|
||||
repeated
|
||||
|
||||
<span>string</span>
|
||||
</code>
|
||||
</td>
|
||||
<td class="content"><p>A collection of IP-based TURN, TURNS, or STUN URLs a client can use to
|
||||
connect to a relay.</p></td>
|
||||
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>2</td>
|
||||
<td>
|
||||
hostname
|
||||
</td>
|
||||
<td>
|
||||
<code title="string">
|
||||
|
||||
optional
|
||||
|
||||
<span>string</span>
|
||||
</code>
|
||||
</td>
|
||||
<td class="content"><p>A hostname clients must use to validate the relay's TLS certificate when
|
||||
connecting via an IP-based TURNS URL. May not be specified if <code>urls</code>
|
||||
contains no TURNS URLs.</p></td>
|
||||
|
||||
</tr>
|
||||
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="block">
|
||||
|
||||
|
||||
<h4 class="title is-4 type-heading" id="message-org.signal.chat.calling.GetCallingRelaysResponse.Relay">
|
||||
<span>
|
||||
<a href="#message-org.signal.chat.calling.GetCallingRelaysResponse.Relay" title="org.signal.chat.calling.GetCallingRelaysResponse.Relay">message Relay</a>
|
||||
|
||||
<br /><span style="font-size: 0.7em; font-weight: normal">(Nested in <a href="org.signal.chat.calling.html#message-org.signal.chat.calling.GetCallingRelaysResponse"><span title="org.signal.chat.calling.GetCallingRelaysResponse">org.signal.chat.calling.GetCallingRelaysResponse</span>
|
||||
</a>)</span>
|
||||
|
||||
</span>
|
||||
|
||||
</h4>
|
||||
|
||||
<div class="block content">
|
||||
|
||||
</div>
|
||||
|
||||
<table class="table is-fullwidth is-hoverable is-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>Field</th>
|
||||
<th>Type</th>
|
||||
<th>Description</th>
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
<tr>
|
||||
<td>1</td>
|
||||
<td>
|
||||
username
|
||||
</td>
|
||||
<td>
|
||||
<code title="string">
|
||||
|
||||
<span>string</span>
|
||||
</code>
|
||||
</td>
|
||||
<td class="content"><p>A username that can be presented to authenticate with a TURN server.</p></td>
|
||||
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>2</td>
|
||||
<td>
|
||||
password
|
||||
</td>
|
||||
<td>
|
||||
<code title="string">
|
||||
|
||||
<span>string</span>
|
||||
</code>
|
||||
</td>
|
||||
<td class="content"><p>A password that can be presented to authenticate with a TURN server.</p></td>
|
||||
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>3</td>
|
||||
<td>
|
||||
credential_ttl_seconds
|
||||
</td>
|
||||
<td>
|
||||
<code title="uint64">
|
||||
|
||||
<span>uint64</span>
|
||||
</code>
|
||||
</td>
|
||||
<td class="content"><p>The duration, in seconds, after which the included username and password
|
||||
will no longer be valid.</p></td>
|
||||
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>4</td>
|
||||
<td>
|
||||
hostname_urls
|
||||
</td>
|
||||
<td>
|
||||
<code title="org.signal.chat.calling.GetCallingRelaysResponse.HostnameUrlList">
|
||||
|
||||
optional
|
||||
|
||||
<a href="org.signal.chat.calling.html#message-org.signal.chat.calling.GetCallingRelaysResponse.HostnameUrlList"><span title="org.signal.chat.calling.GetCallingRelaysResponse.HostnameUrlList">org.signal.chat.calling.GetCallingRelaysResponse.HostnameUrlList</span>
|
||||
</a>
|
||||
</code>
|
||||
</td>
|
||||
<td class="content"><p>A collection of hostname-based URLs clients may use to connect to this
|
||||
relay.</p></td>
|
||||
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>5</td>
|
||||
<td>
|
||||
ip_urls
|
||||
</td>
|
||||
<td>
|
||||
<code title="org.signal.chat.calling.GetCallingRelaysResponse.IpUrlList">
|
||||
|
||||
optional
|
||||
|
||||
<a href="org.signal.chat.calling.html#message-org.signal.chat.calling.GetCallingRelaysResponse.IpUrlList"><span title="org.signal.chat.calling.GetCallingRelaysResponse.IpUrlList">org.signal.chat.calling.GetCallingRelaysResponse.IpUrlList</span>
|
||||
</a>
|
||||
</code>
|
||||
</td>
|
||||
<td class="content"><p>A collection of IP-based URLs clients may use to connect to this relay.</p></td>
|
||||
|
||||
</tr>
|
||||
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<footer class="footer">
|
||||
<div class="content has-text-centered">
|
||||
|
||||
<p>Built with <strong><a href="https://github.com/markvincze/sabledocs" target="_blank">sabledocs</a></strong>.</p>
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
635
grpc/org.signal.chat.calling.quality.html
Normal file
635
grpc/org.signal.chat.calling.quality.html
Normal file
@ -0,0 +1,635 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Protobuf module documentation</title>
|
||||
<link rel="stylesheet" href="static/mystyles.css" />
|
||||
<script src="static/fontawesome.js"></script>
|
||||
<script src="static/lunr.js"></script>
|
||||
<script src="static/theme.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<nav class="level pt-6 mb-5 pb-2 bottom-border">
|
||||
<div class="level-left">
|
||||
<p class="level-item has-text-centered is-size-3 has-text-weight-medium">
|
||||
<a class="link is-info" href="index.html">
|
||||
Protobuf module documentation
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="level-right">
|
||||
|
||||
<form action="search.html" class="mr-2 has-text-centered">
|
||||
<input type="text" name="query" class="input mr-3 navbar-search-query-input" placeholder="Search">
|
||||
<input type="submit" class="button is-primary mr-5" value="Search" />
|
||||
</form>
|
||||
|
||||
|
||||
<p class="level-item has-text-centered is-size-5">
|
||||
<a href="" class="link is-info mr-5" target="_blank">
|
||||
|
||||
<span>Source</span>
|
||||
</a>
|
||||
|
||||
<button class="theme-toggle" id="theme-toggle" title="Toggles light & dark" aria-label="auto" aria-live="polite" type="button">
|
||||
<svg class="sun-and-moon" aria-hidden="true" width="24" height="24" viewBox="0 0 24 24">
|
||||
<mask class="moon" id="moon-mask">
|
||||
<rect x="0" y="0" width="100%" height="100%" fill="white" />
|
||||
<circle cx="24" cy="10" r="6" fill="black" />
|
||||
</mask>
|
||||
<circle class="sun" cx="12" cy="12" r="6" mask="url(#moon-mask)" fill="currentColor" />
|
||||
<g class="sun-beams" stroke="currentColor">
|
||||
<line x1="12" y1="1" x2="12" y2="3" />
|
||||
<line x1="12" y1="21" x2="12" y2="23" />
|
||||
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64" />
|
||||
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78" />
|
||||
<line x1="1" y1="12" x2="3" y2="12" />
|
||||
<line x1="21" y1="12" x2="23" y2="12" />
|
||||
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36" />
|
||||
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22" />
|
||||
</g>
|
||||
</svg>
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<div id="main-content" class="container mb-6">
|
||||
<p class="mb-2">
|
||||
<a style="font-size: 0.9em" href="index.html">← Back to packages</a>
|
||||
</p>
|
||||
<h3 class="title is-3">
|
||||
|
||||
Package <code>org.signal.chat.calling.quality</code>
|
||||
|
||||
</h3>
|
||||
|
||||
<div class="block">
|
||||
|
||||
<p>
|
||||
<a href="#service-org.signal.chat.calling.quality.CallQuality">
|
||||
<span class="tag" style="min-width: 6em">Service</span>
|
||||
<code>CallQuality</code>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
|
||||
<p>
|
||||
<a href="#message-org.signal.chat.calling.quality.SubmitCallQualitySurveyRequest">
|
||||
<span class="tag" style="min-width: 6em">Message</span>
|
||||
<code>SubmitCallQualitySurveyRequest</code>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
<p>
|
||||
<a href="#message-org.signal.chat.calling.quality.SubmitCallQualitySurveyResponse">
|
||||
<span class="tag" style="min-width: 6em">Message</span>
|
||||
<code>SubmitCallQualitySurveyResponse</code>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<div class="block">
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="block">
|
||||
<h3 class="title is-3">
|
||||
Services
|
||||
</h3>
|
||||
|
||||
|
||||
<div>
|
||||
|
||||
|
||||
<h4 class="title is-4 type-heading" id="service-org.signal.chat.calling.quality.CallQuality">
|
||||
<a href="#service-org.signal.chat.calling.quality.CallQuality" title="org.signal.chat.calling.quality.CallQuality">service CallQuality</a>
|
||||
|
||||
</h4>
|
||||
|
||||
<div class="block content">
|
||||
<p><p>Provides methods for submitting call quality surveys</p></p>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="box">
|
||||
<div class="block">
|
||||
<h5 class="title is-5" id="service-org.signal.chat.calling.quality.CallQuality-SubmitCallQualitySurvey">
|
||||
<a href="#service-org.signal.chat.calling.quality.CallQuality-SubmitCallQualitySurvey">
|
||||
rpc <code>SubmitCallQualitySurvey</code>
|
||||
</a>
|
||||
</h5>
|
||||
Request: <code><a href="org.signal.chat.calling.quality.html#message-org.signal.chat.calling.quality.SubmitCallQualitySurveyRequest"><span title="org.signal.chat.calling.quality.SubmitCallQualitySurveyRequest">org.signal.chat.calling.quality.SubmitCallQualitySurveyRequest</span>
|
||||
</a></code><br>
|
||||
Response: <code><a href="org.signal.chat.calling.quality.html#message-org.signal.chat.calling.quality.SubmitCallQualitySurveyResponse"><span title="org.signal.chat.calling.quality.SubmitCallQualitySurveyResponse">org.signal.chat.calling.quality.SubmitCallQualitySurveyResponse</span>
|
||||
</a></code><br>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="block">
|
||||
<p><p>Submits a call quality survey response.</p></p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h3 class="title is-3">
|
||||
Messages
|
||||
</h3>
|
||||
|
||||
|
||||
|
||||
<div class="block">
|
||||
|
||||
|
||||
<h4 class="title is-4 type-heading" id="message-org.signal.chat.calling.quality.SubmitCallQualitySurveyRequest">
|
||||
<span>
|
||||
<a href="#message-org.signal.chat.calling.quality.SubmitCallQualitySurveyRequest" title="org.signal.chat.calling.quality.SubmitCallQualitySurveyRequest">message SubmitCallQualitySurveyRequest</a>
|
||||
|
||||
</span>
|
||||
|
||||
</h4>
|
||||
|
||||
<div class="block content">
|
||||
|
||||
</div>
|
||||
|
||||
<table class="table is-fullwidth is-hoverable is-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>Field</th>
|
||||
<th>Type</th>
|
||||
<th>Description</th>
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
<tr>
|
||||
<td>1</td>
|
||||
<td>
|
||||
user_satisfied
|
||||
</td>
|
||||
<td>
|
||||
<code title="bool">
|
||||
|
||||
<span>bool</span>
|
||||
</code>
|
||||
</td>
|
||||
<td class="content"><p>Indicates whether the caller was generally satisfied with the quality of
|
||||
the call</p></td>
|
||||
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>2</td>
|
||||
<td>
|
||||
call_quality_issues
|
||||
</td>
|
||||
<td>
|
||||
<code title="string">
|
||||
|
||||
repeated
|
||||
|
||||
<span>string</span>
|
||||
</code>
|
||||
</td>
|
||||
<td class="content"><p>A list of call quality issues selected by the caller</p></td>
|
||||
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>3</td>
|
||||
<td>
|
||||
additional_issues_description
|
||||
</td>
|
||||
<td>
|
||||
<code title="string">
|
||||
|
||||
optional
|
||||
|
||||
<span>string</span>
|
||||
</code>
|
||||
</td>
|
||||
<td class="content"><p>A free-form description of any additional issues as written by the caller</p></td>
|
||||
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>4</td>
|
||||
<td>
|
||||
debug_log_url
|
||||
</td>
|
||||
<td>
|
||||
<code title="string">
|
||||
|
||||
optional
|
||||
|
||||
<span>string</span>
|
||||
</code>
|
||||
</td>
|
||||
<td class="content"><p>A URL for a set of debug logs associated with the call if the caller chose
|
||||
to submit debug logs</p></td>
|
||||
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>5</td>
|
||||
<td>
|
||||
start_timestamp
|
||||
</td>
|
||||
<td>
|
||||
<code title="int64">
|
||||
|
||||
<span>int64</span>
|
||||
</code>
|
||||
</td>
|
||||
<td class="content"><p>The time at which the call started in milliseconds since the epoch</p></td>
|
||||
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>6</td>
|
||||
<td>
|
||||
end_timestamp
|
||||
</td>
|
||||
<td>
|
||||
<code title="int64">
|
||||
|
||||
<span>int64</span>
|
||||
</code>
|
||||
</td>
|
||||
<td class="content"><p>The time at which the call ended in milliseconds since the epoch</p></td>
|
||||
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>7</td>
|
||||
<td>
|
||||
call_type
|
||||
</td>
|
||||
<td>
|
||||
<code title="string">
|
||||
|
||||
<span>string</span>
|
||||
</code>
|
||||
</td>
|
||||
<td class="content"><p>The type of call; note that direct voice calls can become video calls and
|
||||
vice versa, and this field indicates which mode was selected at call
|
||||
initiation time. At the time of writing, expected call types are
|
||||
"direct_voice", "direct_video", "group", and "call_link".</p></td>
|
||||
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>8</td>
|
||||
<td>
|
||||
success
|
||||
</td>
|
||||
<td>
|
||||
<code title="bool">
|
||||
|
||||
<span>bool</span>
|
||||
</code>
|
||||
</td>
|
||||
<td class="content"><p>Indicates whether the call completed without error or if it terminated
|
||||
abnormally</p></td>
|
||||
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>9</td>
|
||||
<td>
|
||||
call_end_reason
|
||||
</td>
|
||||
<td>
|
||||
<code title="string">
|
||||
|
||||
<span>string</span>
|
||||
</code>
|
||||
</td>
|
||||
<td class="content"><p>A client-defined, but human-readable reason for call termination</p></td>
|
||||
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>10</td>
|
||||
<td>
|
||||
connection_rtt_median
|
||||
</td>
|
||||
<td>
|
||||
<code title="float">
|
||||
|
||||
optional
|
||||
|
||||
<span>float</span>
|
||||
</code>
|
||||
</td>
|
||||
<td class="content"><p>The median round-trip time, measured in milliseconds, for STUN/ICE packets
|
||||
(i.e. connection maintenance and establishment)</p></td>
|
||||
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>11</td>
|
||||
<td>
|
||||
audio_rtt_median
|
||||
</td>
|
||||
<td>
|
||||
<code title="float">
|
||||
|
||||
optional
|
||||
|
||||
<span>float</span>
|
||||
</code>
|
||||
</td>
|
||||
<td class="content"><p>The median round-trip time, measured in milliseconds, for RTP/RTCP packets
|
||||
for audio streams</p></td>
|
||||
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>12</td>
|
||||
<td>
|
||||
video_rtt_median
|
||||
</td>
|
||||
<td>
|
||||
<code title="float">
|
||||
|
||||
optional
|
||||
|
||||
<span>float</span>
|
||||
</code>
|
||||
</td>
|
||||
<td class="content"><p>The median round-trip time, measured in milliseconds, for RTP/RTCP packets
|
||||
for video streams</p></td>
|
||||
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>13</td>
|
||||
<td>
|
||||
audio_recv_jitter_median
|
||||
</td>
|
||||
<td>
|
||||
<code title="float">
|
||||
|
||||
optional
|
||||
|
||||
<span>float</span>
|
||||
</code>
|
||||
</td>
|
||||
<td class="content"><p>The median jitter for audio streams, measured in milliseconds, for the
|
||||
duration of the call as measured by the client submitting the survey</p></td>
|
||||
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>14</td>
|
||||
<td>
|
||||
video_recv_jitter_median
|
||||
</td>
|
||||
<td>
|
||||
<code title="float">
|
||||
|
||||
optional
|
||||
|
||||
<span>float</span>
|
||||
</code>
|
||||
</td>
|
||||
<td class="content"><p>The median jitter for video streams, measured in milliseconds, for the
|
||||
duration of the call as measured by the client submitting the survey</p></td>
|
||||
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>15</td>
|
||||
<td>
|
||||
audio_send_jitter_median
|
||||
</td>
|
||||
<td>
|
||||
<code title="float">
|
||||
|
||||
optional
|
||||
|
||||
<span>float</span>
|
||||
</code>
|
||||
</td>
|
||||
<td class="content"><p>The median jitter for audio streams, measured in milliseconds, for the
|
||||
duration of the call as measured by the remote endpoint in the call (either
|
||||
the peer of the client submitting the survey in a direct call or the SFU in
|
||||
a group call)</p></td>
|
||||
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>16</td>
|
||||
<td>
|
||||
video_send_jitter_median
|
||||
</td>
|
||||
<td>
|
||||
<code title="float">
|
||||
|
||||
optional
|
||||
|
||||
<span>float</span>
|
||||
</code>
|
||||
</td>
|
||||
<td class="content"><p>The median jitter for video streams, measured in milliseconds, for the
|
||||
duration of the call as measured by the remote endpoint in the call (either
|
||||
the peer of the client submitting the survey in a direct call or the SFU in
|
||||
a group call)</p></td>
|
||||
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>17</td>
|
||||
<td>
|
||||
audio_recv_packet_loss_fraction
|
||||
</td>
|
||||
<td>
|
||||
<code title="float">
|
||||
|
||||
optional
|
||||
|
||||
<span>float</span>
|
||||
</code>
|
||||
</td>
|
||||
<td class="content"><p>The fraction of audio packets lost over the duration of the call as
|
||||
measured by the client submitting the survey</p></td>
|
||||
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>18</td>
|
||||
<td>
|
||||
video_recv_packet_loss_fraction
|
||||
</td>
|
||||
<td>
|
||||
<code title="float">
|
||||
|
||||
optional
|
||||
|
||||
<span>float</span>
|
||||
</code>
|
||||
</td>
|
||||
<td class="content"><p>The fraction of video packets lost over the duration of the call as
|
||||
measured by the client submitting the survey</p></td>
|
||||
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>19</td>
|
||||
<td>
|
||||
audio_send_packet_loss_fraction
|
||||
</td>
|
||||
<td>
|
||||
<code title="float">
|
||||
|
||||
optional
|
||||
|
||||
<span>float</span>
|
||||
</code>
|
||||
</td>
|
||||
<td class="content"><p>The fraction of audio packets lost over the duration of the call as
|
||||
measured by the remote endpoint in the call (either the peer of the client
|
||||
submitting the survey in a direct call or the SFU in a group call)</p></td>
|
||||
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>20</td>
|
||||
<td>
|
||||
video_send_packet_loss_fraction
|
||||
</td>
|
||||
<td>
|
||||
<code title="float">
|
||||
|
||||
optional
|
||||
|
||||
<span>float</span>
|
||||
</code>
|
||||
</td>
|
||||
<td class="content"><p>The fraction of video packets lost over the duration of the call as
|
||||
measured by the remote endpoint in the call (either the peer of the client
|
||||
submitting the survey in a direct call or the SFU in a group call)</p></td>
|
||||
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>21</td>
|
||||
<td>
|
||||
call_telemetry
|
||||
</td>
|
||||
<td>
|
||||
<code title="bytes">
|
||||
|
||||
optional
|
||||
|
||||
<span>bytes</span>
|
||||
</code>
|
||||
</td>
|
||||
<td class="content"><p>Machine-generated telemetry from the call; this is a serialized protobuf
|
||||
entity generated (and, critically, explained to the user!) by the calling
|
||||
library</p></td>
|
||||
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>22</td>
|
||||
<td>
|
||||
call_id_hash
|
||||
</td>
|
||||
<td>
|
||||
<code title="bytes">
|
||||
|
||||
optional
|
||||
|
||||
<span>bytes</span>
|
||||
</code>
|
||||
</td>
|
||||
<td class="content"><p>A hash of a call ID (shared between clients and never sent to the calling
|
||||
server) that can be used to correlate survey responses from multiple
|
||||
participants in a call</p></td>
|
||||
|
||||
</tr>
|
||||
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="block">
|
||||
|
||||
|
||||
<h4 class="title is-4 type-heading" id="message-org.signal.chat.calling.quality.SubmitCallQualitySurveyResponse">
|
||||
<span>
|
||||
<a href="#message-org.signal.chat.calling.quality.SubmitCallQualitySurveyResponse" title="org.signal.chat.calling.quality.SubmitCallQualitySurveyResponse">message SubmitCallQualitySurveyResponse</a>
|
||||
|
||||
</span>
|
||||
|
||||
</h4>
|
||||
|
||||
<div class="block content">
|
||||
|
||||
</div>
|
||||
|
||||
<table class="table is-fullwidth is-hoverable is-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>Field</th>
|
||||
<th>Type</th>
|
||||
<th>Description</th>
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<footer class="footer">
|
||||
<div class="content has-text-centered">
|
||||
|
||||
<p>Built with <strong><a href="https://github.com/markvincze/sabledocs" target="_blank">sabledocs</a></strong>.</p>
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
456
grpc/org.signal.chat.challenge.html
Normal file
456
grpc/org.signal.chat.challenge.html
Normal file
@ -0,0 +1,456 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Protobuf module documentation</title>
|
||||
<link rel="stylesheet" href="static/mystyles.css" />
|
||||
<script src="static/fontawesome.js"></script>
|
||||
<script src="static/lunr.js"></script>
|
||||
<script src="static/theme.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<nav class="level pt-6 mb-5 pb-2 bottom-border">
|
||||
<div class="level-left">
|
||||
<p class="level-item has-text-centered is-size-3 has-text-weight-medium">
|
||||
<a class="link is-info" href="index.html">
|
||||
Protobuf module documentation
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="level-right">
|
||||
|
||||
<form action="search.html" class="mr-2 has-text-centered">
|
||||
<input type="text" name="query" class="input mr-3 navbar-search-query-input" placeholder="Search">
|
||||
<input type="submit" class="button is-primary mr-5" value="Search" />
|
||||
</form>
|
||||
|
||||
|
||||
<p class="level-item has-text-centered is-size-5">
|
||||
<a href="" class="link is-info mr-5" target="_blank">
|
||||
|
||||
<span>Source</span>
|
||||
</a>
|
||||
|
||||
<button class="theme-toggle" id="theme-toggle" title="Toggles light & dark" aria-label="auto" aria-live="polite" type="button">
|
||||
<svg class="sun-and-moon" aria-hidden="true" width="24" height="24" viewBox="0 0 24 24">
|
||||
<mask class="moon" id="moon-mask">
|
||||
<rect x="0" y="0" width="100%" height="100%" fill="white" />
|
||||
<circle cx="24" cy="10" r="6" fill="black" />
|
||||
</mask>
|
||||
<circle class="sun" cx="12" cy="12" r="6" mask="url(#moon-mask)" fill="currentColor" />
|
||||
<g class="sun-beams" stroke="currentColor">
|
||||
<line x1="12" y1="1" x2="12" y2="3" />
|
||||
<line x1="12" y1="21" x2="12" y2="23" />
|
||||
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64" />
|
||||
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78" />
|
||||
<line x1="1" y1="12" x2="3" y2="12" />
|
||||
<line x1="21" y1="12" x2="23" y2="12" />
|
||||
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36" />
|
||||
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22" />
|
||||
</g>
|
||||
</svg>
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<div id="main-content" class="container mb-6">
|
||||
<p class="mb-2">
|
||||
<a style="font-size: 0.9em" href="index.html">← Back to packages</a>
|
||||
</p>
|
||||
<h3 class="title is-3">
|
||||
|
||||
Package <code>org.signal.chat.challenge</code>
|
||||
|
||||
</h3>
|
||||
|
||||
<div class="block">
|
||||
|
||||
<p>
|
||||
<a href="#service-org.signal.chat.challenge.Challenge">
|
||||
<span class="tag" style="min-width: 6em">Service</span>
|
||||
<code>Challenge</code>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
|
||||
<p>
|
||||
<a href="#message-org.signal.chat.challenge.AnswerChallengeRequest.AnswerCaptchaChallengeRequest">
|
||||
<span class="tag" style="min-width: 6em">Message</span>
|
||||
<code>AnswerCaptchaChallengeRequest</code>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
<p>
|
||||
<a href="#message-org.signal.chat.challenge.AnswerChallengeRequest">
|
||||
<span class="tag" style="min-width: 6em">Message</span>
|
||||
<code>AnswerChallengeRequest</code>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
<p>
|
||||
<a href="#message-org.signal.chat.challenge.AnswerChallengeResponse">
|
||||
<span class="tag" style="min-width: 6em">Message</span>
|
||||
<code>AnswerChallengeResponse</code>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
<p>
|
||||
<a href="#message-org.signal.chat.challenge.AnswerChallengeRequest.AnswerPushChallengeRequest">
|
||||
<span class="tag" style="min-width: 6em">Message</span>
|
||||
<code>AnswerPushChallengeRequest</code>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<div class="block">
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="block">
|
||||
<h3 class="title is-3">
|
||||
Services
|
||||
</h3>
|
||||
|
||||
|
||||
<div>
|
||||
|
||||
|
||||
<h4 class="title is-4 type-heading" id="service-org.signal.chat.challenge.Challenge">
|
||||
<a href="#service-org.signal.chat.challenge.Challenge" title="org.signal.chat.challenge.Challenge">service Challenge</a>
|
||||
|
||||
</h4>
|
||||
|
||||
<div class="block content">
|
||||
<p></p>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="box">
|
||||
<div class="block">
|
||||
<h5 class="title is-5" id="service-org.signal.chat.challenge.Challenge-HandleChallengeResponse">
|
||||
<a href="#service-org.signal.chat.challenge.Challenge-HandleChallengeResponse">
|
||||
rpc <code>HandleChallengeResponse</code>
|
||||
</a>
|
||||
</h5>
|
||||
Request: <code><a href="org.signal.chat.challenge.html#message-org.signal.chat.challenge.AnswerChallengeRequest"><span title="org.signal.chat.challenge.AnswerChallengeRequest">org.signal.chat.challenge.AnswerChallengeRequest</span>
|
||||
</a></code><br>
|
||||
Response: <code><a href="org.signal.chat.challenge.html#message-org.signal.chat.challenge.AnswerChallengeResponse"><span title="org.signal.chat.challenge.AnswerChallengeResponse">org.signal.chat.challenge.AnswerChallengeResponse</span>
|
||||
</a></code><br>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="block">
|
||||
<p><p>Some server endpoints (the "send message" endpoint, for example) may return
|
||||
a response indicating the client must complete a challenge before continuing.
|
||||
Clients may use this endpoint to provide proof of a completed challenge.
|
||||
If successful, the client may then continue their original operation.</p></p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h3 class="title is-3">
|
||||
Messages
|
||||
</h3>
|
||||
|
||||
|
||||
|
||||
<div class="block">
|
||||
|
||||
|
||||
<h4 class="title is-4 type-heading" id="message-org.signal.chat.challenge.AnswerChallengeRequest.AnswerCaptchaChallengeRequest">
|
||||
<span>
|
||||
<a href="#message-org.signal.chat.challenge.AnswerChallengeRequest.AnswerCaptchaChallengeRequest" title="org.signal.chat.challenge.AnswerChallengeRequest.AnswerCaptchaChallengeRequest">message AnswerCaptchaChallengeRequest</a>
|
||||
|
||||
<br /><span style="font-size: 0.7em; font-weight: normal">(Nested in <a href="org.signal.chat.challenge.html#message-org.signal.chat.challenge.AnswerChallengeRequest"><span title="org.signal.chat.challenge.AnswerChallengeRequest">org.signal.chat.challenge.AnswerChallengeRequest</span>
|
||||
</a>)</span>
|
||||
|
||||
</span>
|
||||
|
||||
</h4>
|
||||
|
||||
<div class="block content">
|
||||
|
||||
</div>
|
||||
|
||||
<table class="table is-fullwidth is-hoverable is-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>Field</th>
|
||||
<th>Type</th>
|
||||
<th>Description</th>
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
<tr>
|
||||
<td>1</td>
|
||||
<td>
|
||||
captcha
|
||||
</td>
|
||||
<td>
|
||||
<code title="string">
|
||||
|
||||
<span>string</span>
|
||||
</code>
|
||||
</td>
|
||||
<td class="content"><p>A string representing a solved captcha
|
||||
Example: signal-hcaptcha.30b01b46-d8c9-4c30-bbd7-9719acfe0c10.challenge.abcdefg1345</p></td>
|
||||
|
||||
</tr>
|
||||
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="block">
|
||||
|
||||
|
||||
<h4 class="title is-4 type-heading" id="message-org.signal.chat.challenge.AnswerChallengeRequest">
|
||||
<span>
|
||||
<a href="#message-org.signal.chat.challenge.AnswerChallengeRequest" title="org.signal.chat.challenge.AnswerChallengeRequest">message AnswerChallengeRequest</a>
|
||||
|
||||
</span>
|
||||
|
||||
</h4>
|
||||
|
||||
<div class="block content">
|
||||
|
||||
</div>
|
||||
|
||||
<table class="table is-fullwidth is-hoverable is-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>Field</th>
|
||||
<th>Type</th>
|
||||
<th>Description</th>
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
<tr>
|
||||
<td>1</td>
|
||||
<td>
|
||||
token
|
||||
</td>
|
||||
<td>
|
||||
<code title="string">
|
||||
|
||||
<span>string</span>
|
||||
</code>
|
||||
</td>
|
||||
<td class="content"><p>The opaque token id from the ChallengeRequired response returned by the
|
||||
server endpoint that requested the challenge</p></td>
|
||||
|
||||
</tr>
|
||||
|
||||
|
||||
|
||||
|
||||
<tr class="nohover"><td style="border: 0px"></td></tr>
|
||||
<tr class="nohover">
|
||||
<td class="oneof-heading" colspan="4">
|
||||
<div class="oneof-heading">
|
||||
<strong>oneof request</strong>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
2
|
||||
</td>
|
||||
<td>
|
||||
push
|
||||
</td>
|
||||
<td>
|
||||
<code title="org.signal.chat.challenge.AnswerChallengeRequest.AnswerPushChallengeRequest">
|
||||
|
||||
<a href="org.signal.chat.challenge.html#message-org.signal.chat.challenge.AnswerChallengeRequest.AnswerPushChallengeRequest"><span title="org.signal.chat.challenge.AnswerChallengeRequest.AnswerPushChallengeRequest">org.signal.chat.challenge.AnswerChallengeRequest.AnswerPushChallengeRequest</span>
|
||||
</a>
|
||||
</code>
|
||||
</td>
|
||||
<td class="content"></td>
|
||||
|
||||
</tr>
|
||||
|
||||
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
3
|
||||
</td>
|
||||
<td>
|
||||
captcha
|
||||
</td>
|
||||
<td>
|
||||
<code title="org.signal.chat.challenge.AnswerChallengeRequest.AnswerCaptchaChallengeRequest">
|
||||
|
||||
<a href="org.signal.chat.challenge.html#message-org.signal.chat.challenge.AnswerChallengeRequest.AnswerCaptchaChallengeRequest"><span title="org.signal.chat.challenge.AnswerChallengeRequest.AnswerCaptchaChallengeRequest">org.signal.chat.challenge.AnswerChallengeRequest.AnswerCaptchaChallengeRequest</span>
|
||||
</a>
|
||||
</code>
|
||||
</td>
|
||||
<td class="content"></td>
|
||||
|
||||
</tr>
|
||||
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="block">
|
||||
|
||||
|
||||
<h4 class="title is-4 type-heading" id="message-org.signal.chat.challenge.AnswerChallengeResponse">
|
||||
<span>
|
||||
<a href="#message-org.signal.chat.challenge.AnswerChallengeResponse" title="org.signal.chat.challenge.AnswerChallengeResponse">message AnswerChallengeResponse</a>
|
||||
|
||||
</span>
|
||||
|
||||
</h4>
|
||||
|
||||
<div class="block content">
|
||||
|
||||
</div>
|
||||
|
||||
<table class="table is-fullwidth is-hoverable is-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>Field</th>
|
||||
<th>Type</th>
|
||||
<th>Description</th>
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
<tr>
|
||||
<td>1</td>
|
||||
<td>
|
||||
success
|
||||
</td>
|
||||
<td>
|
||||
<code title="bool">
|
||||
|
||||
<span>bool</span>
|
||||
</code>
|
||||
</td>
|
||||
<td class="content"><p>Whether the challenge proof was accepted</p></td>
|
||||
|
||||
</tr>
|
||||
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="block">
|
||||
|
||||
|
||||
<h4 class="title is-4 type-heading" id="message-org.signal.chat.challenge.AnswerChallengeRequest.AnswerPushChallengeRequest">
|
||||
<span>
|
||||
<a href="#message-org.signal.chat.challenge.AnswerChallengeRequest.AnswerPushChallengeRequest" title="org.signal.chat.challenge.AnswerChallengeRequest.AnswerPushChallengeRequest">message AnswerPushChallengeRequest</a>
|
||||
|
||||
<br /><span style="font-size: 0.7em; font-weight: normal">(Nested in <a href="org.signal.chat.challenge.html#message-org.signal.chat.challenge.AnswerChallengeRequest"><span title="org.signal.chat.challenge.AnswerChallengeRequest">org.signal.chat.challenge.AnswerChallengeRequest</span>
|
||||
</a>)</span>
|
||||
|
||||
</span>
|
||||
|
||||
</h4>
|
||||
|
||||
<div class="block content">
|
||||
|
||||
</div>
|
||||
|
||||
<table class="table is-fullwidth is-hoverable is-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>Field</th>
|
||||
<th>Type</th>
|
||||
<th>Description</th>
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
<tr>
|
||||
<td>1</td>
|
||||
<td>
|
||||
challenge
|
||||
</td>
|
||||
<td>
|
||||
<code title="string">
|
||||
|
||||
<span>string</span>
|
||||
</code>
|
||||
</td>
|
||||
<td class="content"><p>The challenge string provided to the client via a push payload</p></td>
|
||||
|
||||
</tr>
|
||||
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<footer class="footer">
|
||||
<div class="content has-text-centered">
|
||||
|
||||
<p>Built with <strong><a href="https://github.com/markvincze/sabledocs" target="_blank">sabledocs</a></strong>.</p>
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
1227
grpc/org.signal.chat.common.html
Normal file
1227
grpc/org.signal.chat.common.html
Normal file
File diff suppressed because it is too large
Load Diff
1182
grpc/org.signal.chat.credentials.html
Normal file
1182
grpc/org.signal.chat.credentials.html
Normal file
File diff suppressed because it is too large
Load Diff
1230
grpc/org.signal.chat.device.html
Normal file
1230
grpc/org.signal.chat.device.html
Normal file
File diff suppressed because it is too large
Load Diff
503
grpc/org.signal.chat.donations.html
Normal file
503
grpc/org.signal.chat.donations.html
Normal file
@ -0,0 +1,503 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Protobuf module documentation</title>
|
||||
<link rel="stylesheet" href="static/mystyles.css" />
|
||||
<script src="static/fontawesome.js"></script>
|
||||
<script src="static/lunr.js"></script>
|
||||
<script src="static/theme.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<nav class="level pt-6 mb-5 pb-2 bottom-border">
|
||||
<div class="level-left">
|
||||
<p class="level-item has-text-centered is-size-3 has-text-weight-medium">
|
||||
<a class="link is-info" href="index.html">
|
||||
Protobuf module documentation
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="level-right">
|
||||
|
||||
<form action="search.html" class="mr-2 has-text-centered">
|
||||
<input type="text" name="query" class="input mr-3 navbar-search-query-input" placeholder="Search">
|
||||
<input type="submit" class="button is-primary mr-5" value="Search" />
|
||||
</form>
|
||||
|
||||
|
||||
<p class="level-item has-text-centered is-size-5">
|
||||
<a href="" class="link is-info mr-5" target="_blank">
|
||||
|
||||
<span>Source</span>
|
||||
</a>
|
||||
|
||||
<button class="theme-toggle" id="theme-toggle" title="Toggles light & dark" aria-label="auto" aria-live="polite" type="button">
|
||||
<svg class="sun-and-moon" aria-hidden="true" width="24" height="24" viewBox="0 0 24 24">
|
||||
<mask class="moon" id="moon-mask">
|
||||
<rect x="0" y="0" width="100%" height="100%" fill="white" />
|
||||
<circle cx="24" cy="10" r="6" fill="black" />
|
||||
</mask>
|
||||
<circle class="sun" cx="12" cy="12" r="6" mask="url(#moon-mask)" fill="currentColor" />
|
||||
<g class="sun-beams" stroke="currentColor">
|
||||
<line x1="12" y1="1" x2="12" y2="3" />
|
||||
<line x1="12" y1="21" x2="12" y2="23" />
|
||||
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64" />
|
||||
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78" />
|
||||
<line x1="1" y1="12" x2="3" y2="12" />
|
||||
<line x1="21" y1="12" x2="23" y2="12" />
|
||||
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36" />
|
||||
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22" />
|
||||
</g>
|
||||
</svg>
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<div id="main-content" class="container mb-6">
|
||||
<p class="mb-2">
|
||||
<a style="font-size: 0.9em" href="index.html">← Back to packages</a>
|
||||
</p>
|
||||
<h3 class="title is-3">
|
||||
|
||||
Package <code>org.signal.chat.donations</code>
|
||||
|
||||
</h3>
|
||||
|
||||
<div class="block">
|
||||
|
||||
<p>
|
||||
<a href="#service-org.signal.chat.donations.Donations">
|
||||
<span class="tag" style="min-width: 6em">Service</span>
|
||||
<code>Donations</code>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
|
||||
<p>
|
||||
<a href="#message-org.signal.chat.donations.CreateDonationPermitRequest">
|
||||
<span class="tag" style="min-width: 6em">Message</span>
|
||||
<code>CreateDonationPermitRequest</code>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
<p>
|
||||
<a href="#message-org.signal.chat.donations.CreateDonationPermitResponse">
|
||||
<span class="tag" style="min-width: 6em">Message</span>
|
||||
<code>CreateDonationPermitResponse</code>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
<p>
|
||||
<a href="#message-org.signal.chat.donations.RedeemReceiptRequest">
|
||||
<span class="tag" style="min-width: 6em">Message</span>
|
||||
<code>RedeemReceiptRequest</code>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
<p>
|
||||
<a href="#message-org.signal.chat.donations.RedeemReceiptResponse">
|
||||
<span class="tag" style="min-width: 6em">Message</span>
|
||||
<code>RedeemReceiptResponse</code>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<div class="block">
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="block">
|
||||
<h3 class="title is-3">
|
||||
Services
|
||||
</h3>
|
||||
|
||||
|
||||
<div>
|
||||
|
||||
|
||||
<h4 class="title is-4 type-heading" id="service-org.signal.chat.donations.Donations">
|
||||
<a href="#service-org.signal.chat.donations.Donations" title="org.signal.chat.donations.Donations">service Donations</a>
|
||||
|
||||
</h4>
|
||||
|
||||
<div class="block content">
|
||||
<p></p>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="box">
|
||||
<div class="block">
|
||||
<h5 class="title is-5" id="service-org.signal.chat.donations.Donations-RedeemReceipt">
|
||||
<a href="#service-org.signal.chat.donations.Donations-RedeemReceipt">
|
||||
rpc <code>RedeemReceipt</code>
|
||||
</a>
|
||||
</h5>
|
||||
Request: <code><a href="org.signal.chat.donations.html#message-org.signal.chat.donations.RedeemReceiptRequest"><span title="org.signal.chat.donations.RedeemReceiptRequest">org.signal.chat.donations.RedeemReceiptRequest</span>
|
||||
</a></code><br>
|
||||
Response: <code><a href="org.signal.chat.donations.html#message-org.signal.chat.donations.RedeemReceiptResponse"><span title="org.signal.chat.donations.RedeemReceiptResponse">org.signal.chat.donations.RedeemReceiptResponse</span>
|
||||
</a></code><br>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="block">
|
||||
<p><p>Redeem a receipt acquired from Subscriptions.CreateSubscriptionReceiptCredentials
|
||||
to add a badge to the account. After successful redemption, profile
|
||||
responses will include the corresponding badge (if configured as visible)
|
||||
until the expiration time on the receipt.</p></p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="box">
|
||||
<div class="block">
|
||||
<h5 class="title is-5" id="service-org.signal.chat.donations.Donations-CreateDonationPermit">
|
||||
<a href="#service-org.signal.chat.donations.Donations-CreateDonationPermit">
|
||||
rpc <code>CreateDonationPermit</code>
|
||||
</a>
|
||||
</h5>
|
||||
Request: <code><a href="org.signal.chat.donations.html#message-org.signal.chat.donations.CreateDonationPermitRequest"><span title="org.signal.chat.donations.CreateDonationPermitRequest">org.signal.chat.donations.CreateDonationPermitRequest</span>
|
||||
</a></code><br>
|
||||
Response: <code><a href="org.signal.chat.donations.html#message-org.signal.chat.donations.CreateDonationPermitResponse"><span title="org.signal.chat.donations.CreateDonationPermitResponse">org.signal.chat.donations.CreateDonationPermitResponse</span>
|
||||
</a></code><br>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="block">
|
||||
<p><p>Generate a set of anonymous, single-use, permits for use with /v1/subscription endpoints.</p>
|
||||
<p>If rate limited, reduce requested permit count and/or try again after the prescribed delay.</p></p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h3 class="title is-3">
|
||||
Messages
|
||||
</h3>
|
||||
|
||||
|
||||
|
||||
<div class="block">
|
||||
|
||||
|
||||
<h4 class="title is-4 type-heading" id="message-org.signal.chat.donations.CreateDonationPermitRequest">
|
||||
<span>
|
||||
<a href="#message-org.signal.chat.donations.CreateDonationPermitRequest" title="org.signal.chat.donations.CreateDonationPermitRequest">message CreateDonationPermitRequest</a>
|
||||
|
||||
</span>
|
||||
|
||||
</h4>
|
||||
|
||||
<div class="block content">
|
||||
|
||||
</div>
|
||||
|
||||
<table class="table is-fullwidth is-hoverable is-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>Field</th>
|
||||
<th>Type</th>
|
||||
<th>Description</th>
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
<tr>
|
||||
<td>1</td>
|
||||
<td>
|
||||
donation_permit_request
|
||||
</td>
|
||||
<td>
|
||||
<code title="bytes">
|
||||
|
||||
<span>bytes</span>
|
||||
</code>
|
||||
</td>
|
||||
<td class="content"><p>a serialized libsignal DonationPermitRequest</p></td>
|
||||
|
||||
</tr>
|
||||
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="block">
|
||||
|
||||
|
||||
<h4 class="title is-4 type-heading" id="message-org.signal.chat.donations.CreateDonationPermitResponse">
|
||||
<span>
|
||||
<a href="#message-org.signal.chat.donations.CreateDonationPermitResponse" title="org.signal.chat.donations.CreateDonationPermitResponse">message CreateDonationPermitResponse</a>
|
||||
|
||||
</span>
|
||||
|
||||
</h4>
|
||||
|
||||
<div class="block content">
|
||||
|
||||
</div>
|
||||
|
||||
<table class="table is-fullwidth is-hoverable is-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>Field</th>
|
||||
<th>Type</th>
|
||||
<th>Description</th>
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
<tr>
|
||||
<td>1</td>
|
||||
<td>
|
||||
donation_permit_response
|
||||
</td>
|
||||
<td>
|
||||
<code title="bytes">
|
||||
|
||||
<span>bytes</span>
|
||||
</code>
|
||||
</td>
|
||||
<td class="content"><p>a serialized libsignal DonationPermitResponse</p></td>
|
||||
|
||||
</tr>
|
||||
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="block">
|
||||
|
||||
|
||||
<h4 class="title is-4 type-heading" id="message-org.signal.chat.donations.RedeemReceiptRequest">
|
||||
<span>
|
||||
<a href="#message-org.signal.chat.donations.RedeemReceiptRequest" title="org.signal.chat.donations.RedeemReceiptRequest">message RedeemReceiptRequest</a>
|
||||
|
||||
</span>
|
||||
|
||||
</h4>
|
||||
|
||||
<div class="block content">
|
||||
|
||||
</div>
|
||||
|
||||
<table class="table is-fullwidth is-hoverable is-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>Field</th>
|
||||
<th>Type</th>
|
||||
<th>Description</th>
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
<tr>
|
||||
<td>1</td>
|
||||
<td>
|
||||
receiptCredentialPresentation
|
||||
</td>
|
||||
<td>
|
||||
<code title="bytes">
|
||||
|
||||
<span>bytes</span>
|
||||
</code>
|
||||
</td>
|
||||
<td class="content"><p>Presentation of the ZK receipt acquired when the subscription was created</p></td>
|
||||
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>2</td>
|
||||
<td>
|
||||
visible
|
||||
</td>
|
||||
<td>
|
||||
<code title="bool">
|
||||
|
||||
<span>bool</span>
|
||||
</code>
|
||||
</td>
|
||||
<td class="content"><p>If true, the corresponding badge should be visible on the profile</p></td>
|
||||
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>3</td>
|
||||
<td>
|
||||
primary
|
||||
</td>
|
||||
<td>
|
||||
<code title="bool">
|
||||
|
||||
<span>bool</span>
|
||||
</code>
|
||||
</td>
|
||||
<td class="content"><p>If true, and the new badge is visible, it should be the primary badge on the profile</p></td>
|
||||
|
||||
</tr>
|
||||
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="block">
|
||||
|
||||
|
||||
<h4 class="title is-4 type-heading" id="message-org.signal.chat.donations.RedeemReceiptResponse">
|
||||
<span>
|
||||
<a href="#message-org.signal.chat.donations.RedeemReceiptResponse" title="org.signal.chat.donations.RedeemReceiptResponse">message RedeemReceiptResponse</a>
|
||||
|
||||
</span>
|
||||
|
||||
</h4>
|
||||
|
||||
<div class="block content">
|
||||
|
||||
</div>
|
||||
|
||||
<table class="table is-fullwidth is-hoverable is-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>Field</th>
|
||||
<th>Type</th>
|
||||
<th>Description</th>
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
|
||||
|
||||
|
||||
<tr class="nohover"><td style="border: 0px"></td></tr>
|
||||
<tr class="nohover">
|
||||
<td class="oneof-heading" colspan="4">
|
||||
<div class="oneof-heading">
|
||||
<strong>oneof response</strong>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
1
|
||||
</td>
|
||||
<td>
|
||||
success
|
||||
</td>
|
||||
<td>
|
||||
<code title="google.protobuf.Empty">
|
||||
|
||||
<span>google.protobuf.Empty</span>
|
||||
</code>
|
||||
</td>
|
||||
<td class="content"><p>The receipt was successfully redeemed</p></td>
|
||||
|
||||
</tr>
|
||||
|
||||
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
2
|
||||
</td>
|
||||
<td>
|
||||
failed_authentication
|
||||
</td>
|
||||
<td>
|
||||
<code title="org.signal.chat.errors.FailedZkAuthentication">
|
||||
|
||||
<a href="org.signal.chat.errors.html#message-org.signal.chat.errors.FailedZkAuthentication"><span title="org.signal.chat.errors.FailedZkAuthentication">org.signal.chat.errors.FailedZkAuthentication</span>
|
||||
</a>
|
||||
</code>
|
||||
</td>
|
||||
<td class="content"><p>The provided presentation is invalid</p></td>
|
||||
|
||||
</tr>
|
||||
|
||||
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
3
|
||||
</td>
|
||||
<td>
|
||||
already_redeemed
|
||||
</td>
|
||||
<td>
|
||||
<code title="org.signal.chat.errors.FailedPrecondition">
|
||||
|
||||
<a href="org.signal.chat.errors.html#message-org.signal.chat.errors.FailedPrecondition"><span title="org.signal.chat.errors.FailedPrecondition">org.signal.chat.errors.FailedPrecondition</span>
|
||||
</a>
|
||||
</code>
|
||||
</td>
|
||||
<td class="content"><p>The receipt was already redeemed for a different account</p></td>
|
||||
|
||||
</tr>
|
||||
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<footer class="footer">
|
||||
<div class="content has-text-centered">
|
||||
|
||||
<p>Built with <strong><a href="https://github.com/markvincze/sabledocs" target="_blank">sabledocs</a></strong>.</p>
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
330
grpc/org.signal.chat.errors.html
Normal file
330
grpc/org.signal.chat.errors.html
Normal file
@ -0,0 +1,330 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Protobuf module documentation</title>
|
||||
<link rel="stylesheet" href="static/mystyles.css" />
|
||||
<script src="static/fontawesome.js"></script>
|
||||
<script src="static/lunr.js"></script>
|
||||
<script src="static/theme.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<nav class="level pt-6 mb-5 pb-2 bottom-border">
|
||||
<div class="level-left">
|
||||
<p class="level-item has-text-centered is-size-3 has-text-weight-medium">
|
||||
<a class="link is-info" href="index.html">
|
||||
Protobuf module documentation
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="level-right">
|
||||
|
||||
<form action="search.html" class="mr-2 has-text-centered">
|
||||
<input type="text" name="query" class="input mr-3 navbar-search-query-input" placeholder="Search">
|
||||
<input type="submit" class="button is-primary mr-5" value="Search" />
|
||||
</form>
|
||||
|
||||
|
||||
<p class="level-item has-text-centered is-size-5">
|
||||
<a href="" class="link is-info mr-5" target="_blank">
|
||||
|
||||
<span>Source</span>
|
||||
</a>
|
||||
|
||||
<button class="theme-toggle" id="theme-toggle" title="Toggles light & dark" aria-label="auto" aria-live="polite" type="button">
|
||||
<svg class="sun-and-moon" aria-hidden="true" width="24" height="24" viewBox="0 0 24 24">
|
||||
<mask class="moon" id="moon-mask">
|
||||
<rect x="0" y="0" width="100%" height="100%" fill="white" />
|
||||
<circle cx="24" cy="10" r="6" fill="black" />
|
||||
</mask>
|
||||
<circle class="sun" cx="12" cy="12" r="6" mask="url(#moon-mask)" fill="currentColor" />
|
||||
<g class="sun-beams" stroke="currentColor">
|
||||
<line x1="12" y1="1" x2="12" y2="3" />
|
||||
<line x1="12" y1="21" x2="12" y2="23" />
|
||||
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64" />
|
||||
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78" />
|
||||
<line x1="1" y1="12" x2="3" y2="12" />
|
||||
<line x1="21" y1="12" x2="23" y2="12" />
|
||||
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36" />
|
||||
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22" />
|
||||
</g>
|
||||
</svg>
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<div id="main-content" class="container mb-6">
|
||||
<p class="mb-2">
|
||||
<a style="font-size: 0.9em" href="index.html">← Back to packages</a>
|
||||
</p>
|
||||
<h3 class="title is-3">
|
||||
|
||||
Package <code>org.signal.chat.errors</code>
|
||||
|
||||
</h3>
|
||||
|
||||
<div class="block">
|
||||
|
||||
|
||||
|
||||
|
||||
<p>
|
||||
<a href="#message-org.signal.chat.errors.FailedPrecondition">
|
||||
<span class="tag" style="min-width: 6em">Message</span>
|
||||
<code>FailedPrecondition</code>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
<p>
|
||||
<a href="#message-org.signal.chat.errors.FailedUnidentifiedAuthorization">
|
||||
<span class="tag" style="min-width: 6em">Message</span>
|
||||
<code>FailedUnidentifiedAuthorization</code>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
<p>
|
||||
<a href="#message-org.signal.chat.errors.FailedZkAuthentication">
|
||||
<span class="tag" style="min-width: 6em">Message</span>
|
||||
<code>FailedZkAuthentication</code>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
<p>
|
||||
<a href="#message-org.signal.chat.errors.NotFound">
|
||||
<span class="tag" style="min-width: 6em">Message</span>
|
||||
<code>NotFound</code>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<div class="block">
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h3 class="title is-3">
|
||||
Messages
|
||||
</h3>
|
||||
|
||||
|
||||
|
||||
<div class="block">
|
||||
|
||||
|
||||
<h4 class="title is-4 type-heading" id="message-org.signal.chat.errors.FailedPrecondition">
|
||||
<span>
|
||||
<a href="#message-org.signal.chat.errors.FailedPrecondition" title="org.signal.chat.errors.FailedPrecondition">message FailedPrecondition</a>
|
||||
|
||||
</span>
|
||||
|
||||
</h4>
|
||||
|
||||
<div class="block content">
|
||||
<p>Response message that indicates that some precondition of the request was not
|
||||
met. For example, if there was a request to update foo, but foo had not been
|
||||
set, this would be an appropriate error.</p>
|
||||
</div>
|
||||
|
||||
<table class="table is-fullwidth is-hoverable is-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>Field</th>
|
||||
<th>Type</th>
|
||||
<th>Description</th>
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
<tr>
|
||||
<td>1</td>
|
||||
<td>
|
||||
description
|
||||
</td>
|
||||
<td>
|
||||
<code title="string">
|
||||
|
||||
<span>string</span>
|
||||
</code>
|
||||
</td>
|
||||
<td class="content"><p>An optional description indicating what precondition failed.</p></td>
|
||||
|
||||
</tr>
|
||||
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="block">
|
||||
|
||||
|
||||
<h4 class="title is-4 type-heading" id="message-org.signal.chat.errors.FailedUnidentifiedAuthorization">
|
||||
<span>
|
||||
<a href="#message-org.signal.chat.errors.FailedUnidentifiedAuthorization" title="org.signal.chat.errors.FailedUnidentifiedAuthorization">message FailedUnidentifiedAuthorization</a>
|
||||
|
||||
</span>
|
||||
|
||||
</h4>
|
||||
|
||||
<div class="block content">
|
||||
<p>Response message that indicates authorization to perform an unidentified
|
||||
operation via an endorsement or access key failed</p>
|
||||
</div>
|
||||
|
||||
<table class="table is-fullwidth is-hoverable is-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>Field</th>
|
||||
<th>Type</th>
|
||||
<th>Description</th>
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
<tr>
|
||||
<td>1</td>
|
||||
<td>
|
||||
description
|
||||
</td>
|
||||
<td>
|
||||
<code title="string">
|
||||
|
||||
<span>string</span>
|
||||
</code>
|
||||
</td>
|
||||
<td class="content"><p>An optional description with additional information about the failure.</p></td>
|
||||
|
||||
</tr>
|
||||
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="block">
|
||||
|
||||
|
||||
<h4 class="title is-4 type-heading" id="message-org.signal.chat.errors.FailedZkAuthentication">
|
||||
<span>
|
||||
<a href="#message-org.signal.chat.errors.FailedZkAuthentication" title="org.signal.chat.errors.FailedZkAuthentication">message FailedZkAuthentication</a>
|
||||
|
||||
</span>
|
||||
|
||||
</h4>
|
||||
|
||||
<div class="block content">
|
||||
<p>Response message that authentication via an anonymous credential failed.</p>
|
||||
</div>
|
||||
|
||||
<table class="table is-fullwidth is-hoverable is-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>Field</th>
|
||||
<th>Type</th>
|
||||
<th>Description</th>
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
<tr>
|
||||
<td>1</td>
|
||||
<td>
|
||||
description
|
||||
</td>
|
||||
<td>
|
||||
<code title="string">
|
||||
|
||||
<span>string</span>
|
||||
</code>
|
||||
</td>
|
||||
<td class="content"><p>An optional description with additional information about the failure.</p></td>
|
||||
|
||||
</tr>
|
||||
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="block">
|
||||
|
||||
|
||||
<h4 class="title is-4 type-heading" id="message-org.signal.chat.errors.NotFound">
|
||||
<span>
|
||||
<a href="#message-org.signal.chat.errors.NotFound" title="org.signal.chat.errors.NotFound">message NotFound</a>
|
||||
|
||||
</span>
|
||||
|
||||
</h4>
|
||||
|
||||
<div class="block content">
|
||||
<p>Response message that indicates a particular resource was not found.</p>
|
||||
</div>
|
||||
|
||||
<table class="table is-fullwidth is-hoverable is-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>Field</th>
|
||||
<th>Type</th>
|
||||
<th>Description</th>
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<footer class="footer">
|
||||
<div class="content has-text-centered">
|
||||
|
||||
<p>Built with <strong><a href="https://github.com/markvincze/sabledocs" target="_blank">sabledocs</a></strong>.</p>
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
1607
grpc/org.signal.chat.keys.html
Normal file
1607
grpc/org.signal.chat.keys.html
Normal file
File diff suppressed because it is too large
Load Diff
2434
grpc/org.signal.chat.messages.html
Normal file
2434
grpc/org.signal.chat.messages.html
Normal file
File diff suppressed because it is too large
Load Diff
1606
grpc/org.signal.chat.one_time_donations.html
Normal file
1606
grpc/org.signal.chat.one_time_donations.html
Normal file
File diff suppressed because it is too large
Load Diff
356
grpc/org.signal.chat.payments.html
Normal file
356
grpc/org.signal.chat.payments.html
Normal file
@ -0,0 +1,356 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Protobuf module documentation</title>
|
||||
<link rel="stylesheet" href="static/mystyles.css" />
|
||||
<script src="static/fontawesome.js"></script>
|
||||
<script src="static/lunr.js"></script>
|
||||
<script src="static/theme.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<nav class="level pt-6 mb-5 pb-2 bottom-border">
|
||||
<div class="level-left">
|
||||
<p class="level-item has-text-centered is-size-3 has-text-weight-medium">
|
||||
<a class="link is-info" href="index.html">
|
||||
Protobuf module documentation
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="level-right">
|
||||
|
||||
<form action="search.html" class="mr-2 has-text-centered">
|
||||
<input type="text" name="query" class="input mr-3 navbar-search-query-input" placeholder="Search">
|
||||
<input type="submit" class="button is-primary mr-5" value="Search" />
|
||||
</form>
|
||||
|
||||
|
||||
<p class="level-item has-text-centered is-size-5">
|
||||
<a href="" class="link is-info mr-5" target="_blank">
|
||||
|
||||
<span>Source</span>
|
||||
</a>
|
||||
|
||||
<button class="theme-toggle" id="theme-toggle" title="Toggles light & dark" aria-label="auto" aria-live="polite" type="button">
|
||||
<svg class="sun-and-moon" aria-hidden="true" width="24" height="24" viewBox="0 0 24 24">
|
||||
<mask class="moon" id="moon-mask">
|
||||
<rect x="0" y="0" width="100%" height="100%" fill="white" />
|
||||
<circle cx="24" cy="10" r="6" fill="black" />
|
||||
</mask>
|
||||
<circle class="sun" cx="12" cy="12" r="6" mask="url(#moon-mask)" fill="currentColor" />
|
||||
<g class="sun-beams" stroke="currentColor">
|
||||
<line x1="12" y1="1" x2="12" y2="3" />
|
||||
<line x1="12" y1="21" x2="12" y2="23" />
|
||||
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64" />
|
||||
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78" />
|
||||
<line x1="1" y1="12" x2="3" y2="12" />
|
||||
<line x1="21" y1="12" x2="23" y2="12" />
|
||||
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36" />
|
||||
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22" />
|
||||
</g>
|
||||
</svg>
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<div id="main-content" class="container mb-6">
|
||||
<p class="mb-2">
|
||||
<a style="font-size: 0.9em" href="index.html">← Back to packages</a>
|
||||
</p>
|
||||
<h3 class="title is-3">
|
||||
|
||||
Package <code>org.signal.chat.payments</code>
|
||||
|
||||
</h3>
|
||||
|
||||
<div class="block">
|
||||
|
||||
<p>
|
||||
<a href="#service-org.signal.chat.payments.Payments">
|
||||
<span class="tag" style="min-width: 6em">Service</span>
|
||||
<code>Payments</code>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<p>
|
||||
<a href="#message-org.signal.chat.payments.GetCurrencyConversionsResponse.CurrencyConversionEntity">
|
||||
<span class="tag" style="min-width: 6em">Message</span>
|
||||
<code>CurrencyConversionEntity</code>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
<p>
|
||||
<a href="#message-org.signal.chat.payments.GetCurrencyConversionsRequest">
|
||||
<span class="tag" style="min-width: 6em">Message</span>
|
||||
<code>GetCurrencyConversionsRequest</code>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
<p>
|
||||
<a href="#message-org.signal.chat.payments.GetCurrencyConversionsResponse">
|
||||
<span class="tag" style="min-width: 6em">Message</span>
|
||||
<code>GetCurrencyConversionsResponse</code>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<div class="block">
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="block">
|
||||
<h3 class="title is-3">
|
||||
Services
|
||||
</h3>
|
||||
|
||||
|
||||
<div>
|
||||
|
||||
|
||||
<h4 class="title is-4 type-heading" id="service-org.signal.chat.payments.Payments">
|
||||
<a href="#service-org.signal.chat.payments.Payments" title="org.signal.chat.payments.Payments">service Payments</a>
|
||||
|
||||
</h4>
|
||||
|
||||
<div class="block content">
|
||||
<p><p>Provides methods for working with payments.</p></p>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="box">
|
||||
<div class="block">
|
||||
<h5 class="title is-5" id="service-org.signal.chat.payments.Payments-GetCurrencyConversions">
|
||||
<a href="#service-org.signal.chat.payments.Payments-GetCurrencyConversions">
|
||||
rpc <code>GetCurrencyConversions</code>
|
||||
</a>
|
||||
</h5>
|
||||
Request: <code><a href="org.signal.chat.payments.html#message-org.signal.chat.payments.GetCurrencyConversionsRequest"><span title="org.signal.chat.payments.GetCurrencyConversionsRequest">org.signal.chat.payments.GetCurrencyConversionsRequest</span>
|
||||
</a></code><br>
|
||||
Response: <code><a href="org.signal.chat.payments.html#message-org.signal.chat.payments.GetCurrencyConversionsResponse"><span title="org.signal.chat.payments.GetCurrencyConversionsResponse">org.signal.chat.payments.GetCurrencyConversionsResponse</span>
|
||||
</a></code><br>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h3 class="title is-3">
|
||||
Messages
|
||||
</h3>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="block">
|
||||
|
||||
|
||||
<h4 class="title is-4 type-heading" id="message-org.signal.chat.payments.GetCurrencyConversionsResponse.CurrencyConversionEntity">
|
||||
<span>
|
||||
<a href="#message-org.signal.chat.payments.GetCurrencyConversionsResponse.CurrencyConversionEntity" title="org.signal.chat.payments.GetCurrencyConversionsResponse.CurrencyConversionEntity">message CurrencyConversionEntity</a>
|
||||
|
||||
<br /><span style="font-size: 0.7em; font-weight: normal">(Nested in <a href="org.signal.chat.payments.html#message-org.signal.chat.payments.GetCurrencyConversionsResponse"><span title="org.signal.chat.payments.GetCurrencyConversionsResponse">org.signal.chat.payments.GetCurrencyConversionsResponse</span>
|
||||
</a>)</span>
|
||||
|
||||
</span>
|
||||
|
||||
</h4>
|
||||
|
||||
<div class="block content">
|
||||
|
||||
</div>
|
||||
|
||||
<table class="table is-fullwidth is-hoverable is-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>Field</th>
|
||||
<th>Type</th>
|
||||
<th>Description</th>
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
<tr>
|
||||
<td>1</td>
|
||||
<td>
|
||||
base
|
||||
</td>
|
||||
<td>
|
||||
<code title="string">
|
||||
|
||||
<span>string</span>
|
||||
</code>
|
||||
</td>
|
||||
<td class="content"></td>
|
||||
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>2</td>
|
||||
<td>
|
||||
conversions
|
||||
</td>
|
||||
<td>
|
||||
<code title="map<string, string>">
|
||||
|
||||
<span>map<string, string></span>
|
||||
</code>
|
||||
</td>
|
||||
<td class="content"></td>
|
||||
|
||||
</tr>
|
||||
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="block">
|
||||
|
||||
|
||||
<h4 class="title is-4 type-heading" id="message-org.signal.chat.payments.GetCurrencyConversionsRequest">
|
||||
<span>
|
||||
<a href="#message-org.signal.chat.payments.GetCurrencyConversionsRequest" title="org.signal.chat.payments.GetCurrencyConversionsRequest">message GetCurrencyConversionsRequest</a>
|
||||
|
||||
</span>
|
||||
|
||||
</h4>
|
||||
|
||||
<div class="block content">
|
||||
|
||||
</div>
|
||||
|
||||
<table class="table is-fullwidth is-hoverable is-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>Field</th>
|
||||
<th>Type</th>
|
||||
<th>Description</th>
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="block">
|
||||
|
||||
|
||||
<h4 class="title is-4 type-heading" id="message-org.signal.chat.payments.GetCurrencyConversionsResponse">
|
||||
<span>
|
||||
<a href="#message-org.signal.chat.payments.GetCurrencyConversionsResponse" title="org.signal.chat.payments.GetCurrencyConversionsResponse">message GetCurrencyConversionsResponse</a>
|
||||
|
||||
</span>
|
||||
|
||||
</h4>
|
||||
|
||||
<div class="block content">
|
||||
|
||||
</div>
|
||||
|
||||
<table class="table is-fullwidth is-hoverable is-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>Field</th>
|
||||
<th>Type</th>
|
||||
<th>Description</th>
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
<tr>
|
||||
<td>1</td>
|
||||
<td>
|
||||
timestamp
|
||||
</td>
|
||||
<td>
|
||||
<code title="uint64">
|
||||
|
||||
<span>uint64</span>
|
||||
</code>
|
||||
</td>
|
||||
<td class="content"></td>
|
||||
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>2</td>
|
||||
<td>
|
||||
currencies
|
||||
</td>
|
||||
<td>
|
||||
<code title="org.signal.chat.payments.GetCurrencyConversionsResponse.CurrencyConversionEntity">
|
||||
|
||||
repeated
|
||||
|
||||
<a href="org.signal.chat.payments.html#message-org.signal.chat.payments.GetCurrencyConversionsResponse.CurrencyConversionEntity"><span title="org.signal.chat.payments.GetCurrencyConversionsResponse.CurrencyConversionEntity">org.signal.chat.payments.GetCurrencyConversionsResponse.CurrencyConversionEntity</span>
|
||||
</a>
|
||||
</code>
|
||||
</td>
|
||||
<td class="content"></td>
|
||||
|
||||
</tr>
|
||||
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<footer class="footer">
|
||||
<div class="content has-text-centered">
|
||||
|
||||
<p>Built with <strong><a href="https://github.com/markvincze/sabledocs" target="_blank">sabledocs</a></strong>.</p>
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
2615
grpc/org.signal.chat.profile.html
Normal file
2615
grpc/org.signal.chat.profile.html
Normal file
File diff suppressed because it is too large
Load Diff
555
grpc/org.signal.chat.require.html
Normal file
555
grpc/org.signal.chat.require.html
Normal file
@ -0,0 +1,555 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Protobuf module documentation</title>
|
||||
<link rel="stylesheet" href="static/mystyles.css" />
|
||||
<script src="static/fontawesome.js"></script>
|
||||
<script src="static/lunr.js"></script>
|
||||
<script src="static/theme.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<nav class="level pt-6 mb-5 pb-2 bottom-border">
|
||||
<div class="level-left">
|
||||
<p class="level-item has-text-centered is-size-3 has-text-weight-medium">
|
||||
<a class="link is-info" href="index.html">
|
||||
Protobuf module documentation
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="level-right">
|
||||
|
||||
<form action="search.html" class="mr-2 has-text-centered">
|
||||
<input type="text" name="query" class="input mr-3 navbar-search-query-input" placeholder="Search">
|
||||
<input type="submit" class="button is-primary mr-5" value="Search" />
|
||||
</form>
|
||||
|
||||
|
||||
<p class="level-item has-text-centered is-size-5">
|
||||
<a href="" class="link is-info mr-5" target="_blank">
|
||||
|
||||
<span>Source</span>
|
||||
</a>
|
||||
|
||||
<button class="theme-toggle" id="theme-toggle" title="Toggles light & dark" aria-label="auto" aria-live="polite" type="button">
|
||||
<svg class="sun-and-moon" aria-hidden="true" width="24" height="24" viewBox="0 0 24 24">
|
||||
<mask class="moon" id="moon-mask">
|
||||
<rect x="0" y="0" width="100%" height="100%" fill="white" />
|
||||
<circle cx="24" cy="10" r="6" fill="black" />
|
||||
</mask>
|
||||
<circle class="sun" cx="12" cy="12" r="6" mask="url(#moon-mask)" fill="currentColor" />
|
||||
<g class="sun-beams" stroke="currentColor">
|
||||
<line x1="12" y1="1" x2="12" y2="3" />
|
||||
<line x1="12" y1="21" x2="12" y2="23" />
|
||||
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64" />
|
||||
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78" />
|
||||
<line x1="1" y1="12" x2="3" y2="12" />
|
||||
<line x1="21" y1="12" x2="23" y2="12" />
|
||||
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36" />
|
||||
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22" />
|
||||
</g>
|
||||
</svg>
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<div id="main-content" class="container mb-6">
|
||||
<p class="mb-2">
|
||||
<a style="font-size: 0.9em" href="index.html">← Back to packages</a>
|
||||
</p>
|
||||
<h3 class="title is-3">
|
||||
|
||||
Package <code>org.signal.chat.require</code>
|
||||
|
||||
</h3>
|
||||
|
||||
<div class="block">
|
||||
|
||||
|
||||
|
||||
|
||||
<p>
|
||||
<a href="#message-org.signal.chat.require.ElementConstraint">
|
||||
<span class="tag" style="min-width: 6em">Message</span>
|
||||
<code>ElementConstraint</code>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
<p>
|
||||
<a href="#message-org.signal.chat.require.SizeConstraint">
|
||||
<span class="tag" style="min-width: 6em">Message</span>
|
||||
<code>SizeConstraint</code>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
<p>
|
||||
<a href="#message-org.signal.chat.require.ValueRangeConstraint">
|
||||
<span class="tag" style="min-width: 6em">Message</span>
|
||||
<code>ValueRangeConstraint</code>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
|
||||
<p>
|
||||
<a href="#enum-org.signal.chat.require.Auth">
|
||||
<span class="tag" style="min-width: 6em">Enum</span>
|
||||
<code>Auth</code>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<a href="#enum-org.signal.chat.require.IdentityType">
|
||||
<span class="tag" style="min-width: 6em">Enum</span>
|
||||
<code>IdentityType</code>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="block">
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h3 class="title is-3">
|
||||
Messages
|
||||
</h3>
|
||||
|
||||
|
||||
|
||||
<div class="block">
|
||||
|
||||
|
||||
<h4 class="title is-4 type-heading" id="message-org.signal.chat.require.ElementConstraint">
|
||||
<span>
|
||||
<a href="#message-org.signal.chat.require.ElementConstraint" title="org.signal.chat.require.ElementConstraint">message ElementConstraint</a>
|
||||
|
||||
</span>
|
||||
|
||||
</h4>
|
||||
|
||||
<div class="block content">
|
||||
|
||||
</div>
|
||||
|
||||
<table class="table is-fullwidth is-hoverable is-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>Field</th>
|
||||
<th>Type</th>
|
||||
<th>Description</th>
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
<tr>
|
||||
<td>1</td>
|
||||
<td>
|
||||
nonEmpty
|
||||
</td>
|
||||
<td>
|
||||
<code title="bool">
|
||||
|
||||
optional
|
||||
|
||||
<span>bool</span>
|
||||
</code>
|
||||
</td>
|
||||
<td class="content"></td>
|
||||
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>2</td>
|
||||
<td>
|
||||
size
|
||||
</td>
|
||||
<td>
|
||||
<code title="org.signal.chat.require.SizeConstraint">
|
||||
|
||||
optional
|
||||
|
||||
<a href="org.signal.chat.require.html#message-org.signal.chat.require.SizeConstraint"><span title="org.signal.chat.require.SizeConstraint">org.signal.chat.require.SizeConstraint</span>
|
||||
</a>
|
||||
</code>
|
||||
</td>
|
||||
<td class="content"></td>
|
||||
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>3</td>
|
||||
<td>
|
||||
exactlySize
|
||||
</td>
|
||||
<td>
|
||||
<code title="uint32">
|
||||
|
||||
repeated
|
||||
|
||||
<span>uint32</span>
|
||||
</code>
|
||||
</td>
|
||||
<td class="content"></td>
|
||||
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>4</td>
|
||||
<td>
|
||||
e164
|
||||
</td>
|
||||
<td>
|
||||
<code title="bool">
|
||||
|
||||
optional
|
||||
|
||||
<span>bool</span>
|
||||
</code>
|
||||
</td>
|
||||
<td class="content"></td>
|
||||
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>5</td>
|
||||
<td>
|
||||
base64url
|
||||
</td>
|
||||
<td>
|
||||
<code title="bool">
|
||||
|
||||
optional
|
||||
|
||||
<span>bool</span>
|
||||
</code>
|
||||
</td>
|
||||
<td class="content"></td>
|
||||
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>6</td>
|
||||
<td>
|
||||
range
|
||||
</td>
|
||||
<td>
|
||||
<code title="org.signal.chat.require.ValueRangeConstraint">
|
||||
|
||||
optional
|
||||
|
||||
<a href="org.signal.chat.require.html#message-org.signal.chat.require.ValueRangeConstraint"><span title="org.signal.chat.require.ValueRangeConstraint">org.signal.chat.require.ValueRangeConstraint</span>
|
||||
</a>
|
||||
</code>
|
||||
</td>
|
||||
<td class="content"></td>
|
||||
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>7</td>
|
||||
<td>
|
||||
identityType
|
||||
</td>
|
||||
<td>
|
||||
<code title="org.signal.chat.require.IdentityType">
|
||||
|
||||
optional
|
||||
|
||||
<a href="org.signal.chat.require.html#enum-org.signal.chat.require.IdentityType"><span title="org.signal.chat.require.IdentityType">org.signal.chat.require.IdentityType</span>
|
||||
</a>
|
||||
</code>
|
||||
</td>
|
||||
<td class="content"></td>
|
||||
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>8</td>
|
||||
<td>
|
||||
specified
|
||||
</td>
|
||||
<td>
|
||||
<code title="bool">
|
||||
|
||||
optional
|
||||
|
||||
<span>bool</span>
|
||||
</code>
|
||||
</td>
|
||||
<td class="content"></td>
|
||||
|
||||
</tr>
|
||||
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="block">
|
||||
|
||||
|
||||
<h4 class="title is-4 type-heading" id="message-org.signal.chat.require.SizeConstraint">
|
||||
<span>
|
||||
<a href="#message-org.signal.chat.require.SizeConstraint" title="org.signal.chat.require.SizeConstraint">message SizeConstraint</a>
|
||||
|
||||
</span>
|
||||
|
||||
</h4>
|
||||
|
||||
<div class="block content">
|
||||
|
||||
</div>
|
||||
|
||||
<table class="table is-fullwidth is-hoverable is-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>Field</th>
|
||||
<th>Type</th>
|
||||
<th>Description</th>
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
<tr>
|
||||
<td>1</td>
|
||||
<td>
|
||||
min
|
||||
</td>
|
||||
<td>
|
||||
<code title="uint32">
|
||||
|
||||
optional
|
||||
|
||||
<span>uint32</span>
|
||||
</code>
|
||||
</td>
|
||||
<td class="content"></td>
|
||||
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>2</td>
|
||||
<td>
|
||||
max
|
||||
</td>
|
||||
<td>
|
||||
<code title="uint32">
|
||||
|
||||
optional
|
||||
|
||||
<span>uint32</span>
|
||||
</code>
|
||||
</td>
|
||||
<td class="content"></td>
|
||||
|
||||
</tr>
|
||||
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="block">
|
||||
|
||||
|
||||
<h4 class="title is-4 type-heading" id="message-org.signal.chat.require.ValueRangeConstraint">
|
||||
<span>
|
||||
<a href="#message-org.signal.chat.require.ValueRangeConstraint" title="org.signal.chat.require.ValueRangeConstraint">message ValueRangeConstraint</a>
|
||||
|
||||
</span>
|
||||
|
||||
</h4>
|
||||
|
||||
<div class="block content">
|
||||
|
||||
</div>
|
||||
|
||||
<table class="table is-fullwidth is-hoverable is-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>Field</th>
|
||||
<th>Type</th>
|
||||
<th>Description</th>
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
<tr>
|
||||
<td>1</td>
|
||||
<td>
|
||||
min
|
||||
</td>
|
||||
<td>
|
||||
<code title="int64">
|
||||
|
||||
optional
|
||||
|
||||
<span>int64</span>
|
||||
</code>
|
||||
</td>
|
||||
<td class="content"></td>
|
||||
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>2</td>
|
||||
<td>
|
||||
max
|
||||
</td>
|
||||
<td>
|
||||
<code title="int64">
|
||||
|
||||
optional
|
||||
|
||||
<span>int64</span>
|
||||
</code>
|
||||
</td>
|
||||
<td class="content"></td>
|
||||
|
||||
</tr>
|
||||
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h3 class="title is-3">
|
||||
Enums
|
||||
</h3>
|
||||
|
||||
|
||||
<div class="block">
|
||||
<h4 class="title is-4 type-heading" id="enum-org.signal.chat.require.Auth">
|
||||
<a href="#enum-org.signal.chat.require.Auth" title="org.signal.chat.require.Auth">enum Auth</a>
|
||||
|
||||
</h4>
|
||||
|
||||
<div class="block content">
|
||||
<p></p>
|
||||
</div>
|
||||
|
||||
<table class="table is-fullwidth is-hoverable is-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Number</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
<tr>
|
||||
<td><code>AUTH_UNSPECIFIED</code></td>
|
||||
<td>0</td>
|
||||
<td class="content"></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td><code>AUTH_ONLY_AUTHENTICATED</code></td>
|
||||
<td>1</td>
|
||||
<td class="content"></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td><code>AUTH_ONLY_ANONYMOUS</code></td>
|
||||
<td>2</td>
|
||||
<td class="content"></td>
|
||||
</tr>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="block">
|
||||
<h4 class="title is-4 type-heading" id="enum-org.signal.chat.require.IdentityType">
|
||||
<a href="#enum-org.signal.chat.require.IdentityType" title="org.signal.chat.require.IdentityType">enum IdentityType</a>
|
||||
|
||||
</h4>
|
||||
|
||||
<div class="block content">
|
||||
<p><p>This is duplicated from common.proto because:</p>
|
||||
<ol>
|
||||
<li>importing would be a circular dependency</li>
|
||||
<li>the canonical declaration belongs there</li>
|
||||
</ol></p>
|
||||
</div>
|
||||
|
||||
<table class="table is-fullwidth is-hoverable is-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Number</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
<tr>
|
||||
<td><code>IDENTITY_TYPE_UNSPECIFIED</code></td>
|
||||
<td>0</td>
|
||||
<td class="content"></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td><code>IDENTITY_TYPE_ACI</code></td>
|
||||
<td>1</td>
|
||||
<td class="content"></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td><code>IDENTITY_TYPE_PNI</code></td>
|
||||
<td>2</td>
|
||||
<td class="content"></td>
|
||||
</tr>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<footer class="footer">
|
||||
<div class="content has-text-centered">
|
||||
|
||||
<p>Built with <strong><a href="https://github.com/markvincze/sabledocs" target="_blank">sabledocs</a></strong>.</p>
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
4774
grpc/org.signal.chat.subscriptions.html
Normal file
4774
grpc/org.signal.chat.subscriptions.html
Normal file
File diff suppressed because it is too large
Load Diff
98
grpc/org.signal.chat.tag.html
Normal file
98
grpc/org.signal.chat.tag.html
Normal file
@ -0,0 +1,98 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Protobuf module documentation</title>
|
||||
<link rel="stylesheet" href="static/mystyles.css" />
|
||||
<script src="static/fontawesome.js"></script>
|
||||
<script src="static/lunr.js"></script>
|
||||
<script src="static/theme.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<nav class="level pt-6 mb-5 pb-2 bottom-border">
|
||||
<div class="level-left">
|
||||
<p class="level-item has-text-centered is-size-3 has-text-weight-medium">
|
||||
<a class="link is-info" href="index.html">
|
||||
Protobuf module documentation
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="level-right">
|
||||
|
||||
<form action="search.html" class="mr-2 has-text-centered">
|
||||
<input type="text" name="query" class="input mr-3 navbar-search-query-input" placeholder="Search">
|
||||
<input type="submit" class="button is-primary mr-5" value="Search" />
|
||||
</form>
|
||||
|
||||
|
||||
<p class="level-item has-text-centered is-size-5">
|
||||
<a href="" class="link is-info mr-5" target="_blank">
|
||||
|
||||
<span>Source</span>
|
||||
</a>
|
||||
|
||||
<button class="theme-toggle" id="theme-toggle" title="Toggles light & dark" aria-label="auto" aria-live="polite" type="button">
|
||||
<svg class="sun-and-moon" aria-hidden="true" width="24" height="24" viewBox="0 0 24 24">
|
||||
<mask class="moon" id="moon-mask">
|
||||
<rect x="0" y="0" width="100%" height="100%" fill="white" />
|
||||
<circle cx="24" cy="10" r="6" fill="black" />
|
||||
</mask>
|
||||
<circle class="sun" cx="12" cy="12" r="6" mask="url(#moon-mask)" fill="currentColor" />
|
||||
<g class="sun-beams" stroke="currentColor">
|
||||
<line x1="12" y1="1" x2="12" y2="3" />
|
||||
<line x1="12" y1="21" x2="12" y2="23" />
|
||||
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64" />
|
||||
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78" />
|
||||
<line x1="1" y1="12" x2="3" y2="12" />
|
||||
<line x1="21" y1="12" x2="23" y2="12" />
|
||||
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36" />
|
||||
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22" />
|
||||
</g>
|
||||
</svg>
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<div id="main-content" class="container mb-6">
|
||||
<p class="mb-2">
|
||||
<a style="font-size: 0.9em" href="index.html">← Back to packages</a>
|
||||
</p>
|
||||
<h3 class="title is-3">
|
||||
|
||||
Package <code>org.signal.chat.tag</code>
|
||||
|
||||
</h3>
|
||||
|
||||
<div class="block">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<div class="block">
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<footer class="footer">
|
||||
<div class="content has-text-centered">
|
||||
|
||||
<p>Built with <strong><a href="https://github.com/markvincze/sabledocs" target="_blank">sabledocs</a></strong>.</p>
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
144
grpc/search.html
Normal file
144
grpc/search.html
Normal file
File diff suppressed because one or more lines are too long
2
grpc/static/fontawesome.js
Normal file
2
grpc/static/fontawesome.js
Normal file
File diff suppressed because one or more lines are too long
3475
grpc/static/lunr.js
Normal file
3475
grpc/static/lunr.js
Normal file
File diff suppressed because it is too large
Load Diff
8083
grpc/static/mystyles.css
Normal file
8083
grpc/static/mystyles.css
Normal file
File diff suppressed because it is too large
Load Diff
54
grpc/static/theme.js
Normal file
54
grpc/static/theme.js
Normal file
@ -0,0 +1,54 @@
|
||||
const storageKey = 'theme-preference';
|
||||
|
||||
const onClick = () => {
|
||||
// flip current value
|
||||
theme.value = theme.value === 'light'
|
||||
? 'dark'
|
||||
: 'light';
|
||||
|
||||
setPreference();
|
||||
}
|
||||
|
||||
const getColorPreference = () => {
|
||||
if (localStorage.getItem(storageKey))
|
||||
return localStorage.getItem(storageKey);
|
||||
else
|
||||
return window.matchMedia('(prefers-color-scheme: dark)').matches
|
||||
? 'dark'
|
||||
: 'light';
|
||||
}
|
||||
|
||||
const setPreference = () => {
|
||||
localStorage.setItem(storageKey, theme.value);
|
||||
reflectPreference();
|
||||
}
|
||||
|
||||
const reflectPreference = () => {
|
||||
document.documentElement.className =
|
||||
theme.value === 'dark' ? 'dark' : '';
|
||||
|
||||
document.documentElement
|
||||
.setAttribute('data-theme', theme.value);
|
||||
}
|
||||
|
||||
const theme = {
|
||||
value: getColorPreference(),
|
||||
}
|
||||
|
||||
window.addEventListener(
|
||||
"load",
|
||||
() => {
|
||||
document
|
||||
.querySelector('#theme-toggle')
|
||||
.addEventListener('click', onClick);
|
||||
});
|
||||
|
||||
// sync with system changes
|
||||
window
|
||||
.matchMedia('(prefers-color-scheme: dark)')
|
||||
.addEventListener('change', ({ matches: isDark }) => {
|
||||
theme.value = isDark ? 'dark' : 'light';
|
||||
setPreference();
|
||||
});
|
||||
|
||||
reflectPreference();
|
||||
20
index.html
Normal file
20
index.html
Normal file
@ -0,0 +1,20 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<title>Signal Server API</title>
|
||||
|
||||
<script src="https://unpkg.com/@stoplight/elements/web-components.min.js"></script>
|
||||
<link rel="stylesheet" href="https://unpkg.com/@stoplight/elements/styles.min.css">
|
||||
<link rel="icon" type="image/x-icon" href="/favicon.ico">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<elements-api
|
||||
apiDescriptionUrl="signal-server-openapi.yaml"
|
||||
router="hash"
|
||||
/>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
316
mvnw
vendored
316
mvnw
vendored
@ -1,316 +0,0 @@
|
||||
#!/bin/sh
|
||||
# ----------------------------------------------------------------------------
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# Maven Start Up Batch script
|
||||
#
|
||||
# Required ENV vars:
|
||||
# ------------------
|
||||
# JAVA_HOME - location of a JDK home dir
|
||||
#
|
||||
# Optional ENV vars
|
||||
# -----------------
|
||||
# M2_HOME - location of maven2's installed home dir
|
||||
# MAVEN_OPTS - parameters passed to the Java VM when running Maven
|
||||
# e.g. to debug Maven itself, use
|
||||
# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
|
||||
# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
if [ -z "$MAVEN_SKIP_RC" ] ; then
|
||||
|
||||
if [ -f /usr/local/etc/mavenrc ] ; then
|
||||
. /usr/local/etc/mavenrc
|
||||
fi
|
||||
|
||||
if [ -f /etc/mavenrc ] ; then
|
||||
. /etc/mavenrc
|
||||
fi
|
||||
|
||||
if [ -f "$HOME/.mavenrc" ] ; then
|
||||
. "$HOME/.mavenrc"
|
||||
fi
|
||||
|
||||
fi
|
||||
|
||||
# OS specific support. $var _must_ be set to either true or false.
|
||||
cygwin=false;
|
||||
darwin=false;
|
||||
mingw=false
|
||||
case "`uname`" in
|
||||
CYGWIN*) cygwin=true ;;
|
||||
MINGW*) mingw=true;;
|
||||
Darwin*) darwin=true
|
||||
# Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
|
||||
# See https://developer.apple.com/library/mac/qa/qa1170/_index.html
|
||||
if [ -z "$JAVA_HOME" ]; then
|
||||
if [ -x "/usr/libexec/java_home" ]; then
|
||||
export JAVA_HOME="`/usr/libexec/java_home`"
|
||||
else
|
||||
export JAVA_HOME="/Library/Java/Home"
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -z "$JAVA_HOME" ] ; then
|
||||
if [ -r /etc/gentoo-release ] ; then
|
||||
JAVA_HOME=`java-config --jre-home`
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -z "$M2_HOME" ] ; then
|
||||
## resolve links - $0 may be a link to maven's home
|
||||
PRG="$0"
|
||||
|
||||
# need this for relative symlinks
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG="`dirname "$PRG"`/$link"
|
||||
fi
|
||||
done
|
||||
|
||||
saveddir=`pwd`
|
||||
|
||||
M2_HOME=`dirname "$PRG"`/..
|
||||
|
||||
# make it fully qualified
|
||||
M2_HOME=`cd "$M2_HOME" && pwd`
|
||||
|
||||
cd "$saveddir"
|
||||
# echo Using m2 at $M2_HOME
|
||||
fi
|
||||
|
||||
# For Cygwin, ensure paths are in UNIX format before anything is touched
|
||||
if $cygwin ; then
|
||||
[ -n "$M2_HOME" ] &&
|
||||
M2_HOME=`cygpath --unix "$M2_HOME"`
|
||||
[ -n "$JAVA_HOME" ] &&
|
||||
JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
|
||||
[ -n "$CLASSPATH" ] &&
|
||||
CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
|
||||
fi
|
||||
|
||||
# For Mingw, ensure paths are in UNIX format before anything is touched
|
||||
if $mingw ; then
|
||||
[ -n "$M2_HOME" ] &&
|
||||
M2_HOME="`(cd "$M2_HOME"; pwd)`"
|
||||
[ -n "$JAVA_HOME" ] &&
|
||||
JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
|
||||
fi
|
||||
|
||||
if [ -z "$JAVA_HOME" ]; then
|
||||
javaExecutable="`which javac`"
|
||||
if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
|
||||
# readlink(1) is not available as standard on Solaris 10.
|
||||
readLink=`which readlink`
|
||||
if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
|
||||
if $darwin ; then
|
||||
javaHome="`dirname \"$javaExecutable\"`"
|
||||
javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
|
||||
else
|
||||
javaExecutable="`readlink -f \"$javaExecutable\"`"
|
||||
fi
|
||||
javaHome="`dirname \"$javaExecutable\"`"
|
||||
javaHome=`expr "$javaHome" : '\(.*\)/bin'`
|
||||
JAVA_HOME="$javaHome"
|
||||
export JAVA_HOME
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -z "$JAVACMD" ] ; then
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
fi
|
||||
else
|
||||
JAVACMD="`\\unset -f command; \\command -v java`"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
echo "Error: JAVA_HOME is not defined correctly." >&2
|
||||
echo " We cannot execute $JAVACMD" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "$JAVA_HOME" ] ; then
|
||||
echo "Warning: JAVA_HOME environment variable is not set."
|
||||
fi
|
||||
|
||||
CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
|
||||
|
||||
# traverses directory structure from process work directory to filesystem root
|
||||
# first directory with .mvn subdirectory is considered project base directory
|
||||
find_maven_basedir() {
|
||||
|
||||
if [ -z "$1" ]
|
||||
then
|
||||
echo "Path not specified to find_maven_basedir"
|
||||
return 1
|
||||
fi
|
||||
|
||||
basedir="$1"
|
||||
wdir="$1"
|
||||
while [ "$wdir" != '/' ] ; do
|
||||
if [ -d "$wdir"/.mvn ] ; then
|
||||
basedir=$wdir
|
||||
break
|
||||
fi
|
||||
# workaround for JBEAP-8937 (on Solaris 10/Sparc)
|
||||
if [ -d "${wdir}" ]; then
|
||||
wdir=`cd "$wdir/.."; pwd`
|
||||
fi
|
||||
# end of workaround
|
||||
done
|
||||
echo "${basedir}"
|
||||
}
|
||||
|
||||
# concatenates all lines of a file
|
||||
concat_lines() {
|
||||
if [ -f "$1" ]; then
|
||||
echo "$(tr -s '\n' ' ' < "$1")"
|
||||
fi
|
||||
}
|
||||
|
||||
BASE_DIR=`find_maven_basedir "$(pwd)"`
|
||||
if [ -z "$BASE_DIR" ]; then
|
||||
exit 1;
|
||||
fi
|
||||
|
||||
##########################################################################################
|
||||
# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
|
||||
# This allows using the maven wrapper in projects that prohibit checking in binary data.
|
||||
##########################################################################################
|
||||
if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then
|
||||
if [ "$MVNW_VERBOSE" = true ]; then
|
||||
echo "Found .mvn/wrapper/maven-wrapper.jar"
|
||||
fi
|
||||
else
|
||||
if [ "$MVNW_VERBOSE" = true ]; then
|
||||
echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..."
|
||||
fi
|
||||
if [ -n "$MVNW_REPOURL" ]; then
|
||||
jarUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar"
|
||||
else
|
||||
jarUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar"
|
||||
fi
|
||||
while IFS="=" read key value; do
|
||||
case "$key" in (wrapperUrl) jarUrl="$value"; break ;;
|
||||
esac
|
||||
done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties"
|
||||
if [ "$MVNW_VERBOSE" = true ]; then
|
||||
echo "Downloading from: $jarUrl"
|
||||
fi
|
||||
wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar"
|
||||
if $cygwin; then
|
||||
wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"`
|
||||
fi
|
||||
|
||||
if command -v wget > /dev/null; then
|
||||
if [ "$MVNW_VERBOSE" = true ]; then
|
||||
echo "Found wget ... using wget"
|
||||
fi
|
||||
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
|
||||
wget "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
|
||||
else
|
||||
wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
|
||||
fi
|
||||
elif command -v curl > /dev/null; then
|
||||
if [ "$MVNW_VERBOSE" = true ]; then
|
||||
echo "Found curl ... using curl"
|
||||
fi
|
||||
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
|
||||
curl -o "$wrapperJarPath" "$jarUrl" -f
|
||||
else
|
||||
curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f
|
||||
fi
|
||||
|
||||
else
|
||||
if [ "$MVNW_VERBOSE" = true ]; then
|
||||
echo "Falling back to using Java to download"
|
||||
fi
|
||||
javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java"
|
||||
# For Cygwin, switch paths to Windows format before running javac
|
||||
if $cygwin; then
|
||||
javaClass=`cygpath --path --windows "$javaClass"`
|
||||
fi
|
||||
if [ -e "$javaClass" ]; then
|
||||
if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
|
||||
if [ "$MVNW_VERBOSE" = true ]; then
|
||||
echo " - Compiling MavenWrapperDownloader.java ..."
|
||||
fi
|
||||
# Compiling the Java class
|
||||
("$JAVA_HOME/bin/javac" "$javaClass")
|
||||
fi
|
||||
if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
|
||||
# Running the downloader
|
||||
if [ "$MVNW_VERBOSE" = true ]; then
|
||||
echo " - Running MavenWrapperDownloader.java ..."
|
||||
fi
|
||||
("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR")
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
##########################################################################################
|
||||
# End of extension
|
||||
##########################################################################################
|
||||
|
||||
export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
|
||||
if [ "$MVNW_VERBOSE" = true ]; then
|
||||
echo $MAVEN_PROJECTBASEDIR
|
||||
fi
|
||||
MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
|
||||
|
||||
# For Cygwin, switch paths to Windows format before running java
|
||||
if $cygwin; then
|
||||
[ -n "$M2_HOME" ] &&
|
||||
M2_HOME=`cygpath --path --windows "$M2_HOME"`
|
||||
[ -n "$JAVA_HOME" ] &&
|
||||
JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
|
||||
[ -n "$CLASSPATH" ] &&
|
||||
CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
|
||||
[ -n "$MAVEN_PROJECTBASEDIR" ] &&
|
||||
MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
|
||||
fi
|
||||
|
||||
# Provide a "standardized" way to retrieve the CLI args that will
|
||||
# work with both Windows and non-Windows executions.
|
||||
MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@"
|
||||
export MAVEN_CMD_LINE_ARGS
|
||||
|
||||
WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
|
||||
|
||||
exec "$JAVACMD" \
|
||||
$MAVEN_OPTS \
|
||||
$MAVEN_DEBUG_OPTS \
|
||||
-classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
|
||||
"-Dmaven.home=${M2_HOME}" \
|
||||
"-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
|
||||
${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
|
||||
188
mvnw.cmd
vendored
188
mvnw.cmd
vendored
@ -1,188 +0,0 @@
|
||||
@REM ----------------------------------------------------------------------------
|
||||
@REM Licensed to the Apache Software Foundation (ASF) under one
|
||||
@REM or more contributor license agreements. See the NOTICE file
|
||||
@REM distributed with this work for additional information
|
||||
@REM regarding copyright ownership. The ASF licenses this file
|
||||
@REM to you under the Apache License, Version 2.0 (the
|
||||
@REM "License"); you may not use this file except in compliance
|
||||
@REM with the License. You may obtain a copy of the License at
|
||||
@REM
|
||||
@REM http://www.apache.org/licenses/LICENSE-2.0
|
||||
@REM
|
||||
@REM Unless required by applicable law or agreed to in writing,
|
||||
@REM software distributed under the License is distributed on an
|
||||
@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
@REM KIND, either express or implied. See the License for the
|
||||
@REM specific language governing permissions and limitations
|
||||
@REM under the License.
|
||||
@REM ----------------------------------------------------------------------------
|
||||
|
||||
@REM ----------------------------------------------------------------------------
|
||||
@REM Maven Start Up Batch script
|
||||
@REM
|
||||
@REM Required ENV vars:
|
||||
@REM JAVA_HOME - location of a JDK home dir
|
||||
@REM
|
||||
@REM Optional ENV vars
|
||||
@REM M2_HOME - location of maven2's installed home dir
|
||||
@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
|
||||
@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending
|
||||
@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
|
||||
@REM e.g. to debug Maven itself, use
|
||||
@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
|
||||
@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
|
||||
@REM ----------------------------------------------------------------------------
|
||||
|
||||
@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
|
||||
@echo off
|
||||
@REM set title of command window
|
||||
title %0
|
||||
@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
|
||||
@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
|
||||
|
||||
@REM set %HOME% to equivalent of $HOME
|
||||
if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
|
||||
|
||||
@REM Execute a user defined script before this one
|
||||
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
|
||||
@REM check for pre script, once with legacy .bat ending and once with .cmd ending
|
||||
if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %*
|
||||
if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %*
|
||||
:skipRcPre
|
||||
|
||||
@setlocal
|
||||
|
||||
set ERROR_CODE=0
|
||||
|
||||
@REM To isolate internal variables from possible post scripts, we use another setlocal
|
||||
@setlocal
|
||||
|
||||
@REM ==== START VALIDATION ====
|
||||
if not "%JAVA_HOME%" == "" goto OkJHome
|
||||
|
||||
echo.
|
||||
echo Error: JAVA_HOME not found in your environment. >&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the >&2
|
||||
echo location of your Java installation. >&2
|
||||
echo.
|
||||
goto error
|
||||
|
||||
:OkJHome
|
||||
if exist "%JAVA_HOME%\bin\java.exe" goto init
|
||||
|
||||
echo.
|
||||
echo Error: JAVA_HOME is set to an invalid directory. >&2
|
||||
echo JAVA_HOME = "%JAVA_HOME%" >&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the >&2
|
||||
echo location of your Java installation. >&2
|
||||
echo.
|
||||
goto error
|
||||
|
||||
@REM ==== END VALIDATION ====
|
||||
|
||||
:init
|
||||
|
||||
@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
|
||||
@REM Fallback to current working directory if not found.
|
||||
|
||||
set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
|
||||
IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
|
||||
|
||||
set EXEC_DIR=%CD%
|
||||
set WDIR=%EXEC_DIR%
|
||||
:findBaseDir
|
||||
IF EXIST "%WDIR%"\.mvn goto baseDirFound
|
||||
cd ..
|
||||
IF "%WDIR%"=="%CD%" goto baseDirNotFound
|
||||
set WDIR=%CD%
|
||||
goto findBaseDir
|
||||
|
||||
:baseDirFound
|
||||
set MAVEN_PROJECTBASEDIR=%WDIR%
|
||||
cd "%EXEC_DIR%"
|
||||
goto endDetectBaseDir
|
||||
|
||||
:baseDirNotFound
|
||||
set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
|
||||
cd "%EXEC_DIR%"
|
||||
|
||||
:endDetectBaseDir
|
||||
|
||||
IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
|
||||
|
||||
@setlocal EnableExtensions EnableDelayedExpansion
|
||||
for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
|
||||
@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
|
||||
|
||||
:endReadAdditionalConfig
|
||||
|
||||
SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
|
||||
set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
|
||||
set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
|
||||
|
||||
set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar"
|
||||
|
||||
FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
|
||||
IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B
|
||||
)
|
||||
|
||||
@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
|
||||
@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
|
||||
if exist %WRAPPER_JAR% (
|
||||
if "%MVNW_VERBOSE%" == "true" (
|
||||
echo Found %WRAPPER_JAR%
|
||||
)
|
||||
) else (
|
||||
if not "%MVNW_REPOURL%" == "" (
|
||||
SET DOWNLOAD_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar"
|
||||
)
|
||||
if "%MVNW_VERBOSE%" == "true" (
|
||||
echo Couldn't find %WRAPPER_JAR%, downloading it ...
|
||||
echo Downloading from: %DOWNLOAD_URL%
|
||||
)
|
||||
|
||||
powershell -Command "&{"^
|
||||
"$webclient = new-object System.Net.WebClient;"^
|
||||
"if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
|
||||
"$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
|
||||
"}"^
|
||||
"[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^
|
||||
"}"
|
||||
if "%MVNW_VERBOSE%" == "true" (
|
||||
echo Finished downloading %WRAPPER_JAR%
|
||||
)
|
||||
)
|
||||
@REM End of extension
|
||||
|
||||
@REM Provide a "standardized" way to retrieve the CLI args that will
|
||||
@REM work with both Windows and non-Windows executions.
|
||||
set MAVEN_CMD_LINE_ARGS=%*
|
||||
|
||||
%MAVEN_JAVA_EXE% ^
|
||||
%JVM_CONFIG_MAVEN_PROPS% ^
|
||||
%MAVEN_OPTS% ^
|
||||
%MAVEN_DEBUG_OPTS% ^
|
||||
-classpath %WRAPPER_JAR% ^
|
||||
"-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^
|
||||
%WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
|
||||
if ERRORLEVEL 1 goto error
|
||||
goto end
|
||||
|
||||
:error
|
||||
set ERROR_CODE=1
|
||||
|
||||
:end
|
||||
@endlocal & set ERROR_CODE=%ERROR_CODE%
|
||||
|
||||
if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost
|
||||
@REM check for post script, once with legacy .bat ending and once with .cmd ending
|
||||
if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat"
|
||||
if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd"
|
||||
:skipRcPost
|
||||
|
||||
@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
|
||||
if "%MAVEN_BATCH_PAUSE%"=="on" pause
|
||||
|
||||
if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE%
|
||||
|
||||
cmd /C exit /B %ERROR_CODE%
|
||||
511
pom.xml
511
pom.xml
@ -1,511 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>central</id>
|
||||
<name>Central Repository</name>
|
||||
<url>https://repo.maven.apache.org/maven2</url>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>dynamodb-local-oregon</id>
|
||||
<name>DynamoDB Local Release Repository</name>
|
||||
<url>https://s3-us-west-2.amazonaws.com/dynamodb-local/release</url>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<pluginRepositories>
|
||||
<pluginRepository>
|
||||
<id>ossrh-snapshots</id>
|
||||
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
|
||||
<releases>
|
||||
<enabled>false</enabled>
|
||||
</releases>
|
||||
<snapshots>
|
||||
<enabled>true</enabled>
|
||||
</snapshots>
|
||||
</pluginRepository>
|
||||
</pluginRepositories>
|
||||
|
||||
<modules>
|
||||
<module>event-logger</module>
|
||||
<module>redis-dispatch</module>
|
||||
<module>websocket-resources</module>
|
||||
<module>service</module>
|
||||
</modules>
|
||||
|
||||
<properties>
|
||||
<aws.sdk.version>1.12.376</aws.sdk.version>
|
||||
<aws.sdk2.version>2.19.8</aws.sdk2.version>
|
||||
<braintree.version>3.19.0</braintree.version>
|
||||
<commons-csv.version>1.9.0</commons-csv.version>
|
||||
<commons-io.version>2.9.0</commons-io.version>
|
||||
<dropwizard.version>2.0.34</dropwizard.version>
|
||||
<dropwizard-metrics-datadog.version>1.1.13</dropwizard-metrics-datadog.version>
|
||||
<google-cloud-libraries.version>26.1.3</google-cloud-libraries.version>
|
||||
<grpc.version>1.51.1</grpc.version> <!-- this should be kept in sync with the value from Google’s libraries-bom -->
|
||||
<gson.version>2.9.0</gson.version>
|
||||
<jackson.version>2.13.4</jackson.version>
|
||||
<jaxb.version>2.3.1</jaxb.version>
|
||||
<jedis.version>2.9.0</jedis.version>
|
||||
<kotlin.version>1.8.0</kotlin.version>
|
||||
<kotlinx-serialization.version>1.4.1</kotlinx-serialization.version>
|
||||
<lettuce.version>6.2.1.RELEASE</lettuce.version>
|
||||
<libphonenumber.version>8.12.54</libphonenumber.version>
|
||||
<logstash.logback.version>7.2</logstash.logback.version>
|
||||
<micrometer.version>1.10.3</micrometer.version>
|
||||
<mockito.version>4.11.0</mockito.version>
|
||||
<netty.version>4.1.82.Final</netty.version>
|
||||
<opentest4j.version>1.2.0</opentest4j.version>
|
||||
<protobuf.version>3.21.7</protobuf.version>
|
||||
<pushy.version>0.15.2</pushy.version>
|
||||
<resilience4j.version>1.7.0</resilience4j.version>
|
||||
<semver4j.version>3.1.0</semver4j.version>
|
||||
<slf4j.version>1.7.30</slf4j.version>
|
||||
<stripe.version>21.2.0</stripe.version>
|
||||
<vavr.version>0.10.4</vavr.version>
|
||||
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<groupId>org.whispersystems.textsecure</groupId>
|
||||
<artifactId>TextSecureServer</artifactId>
|
||||
<version>JGITVER</version>
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson</groupId>
|
||||
<artifactId>jackson-bom</artifactId>
|
||||
<version>${jackson.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.dropwizard</groupId>
|
||||
<artifactId>dropwizard-dependencies</artifactId>
|
||||
<version>${dropwizard.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
<!-- Needed for gRPC with Java 9+ -->
|
||||
<dependency>
|
||||
<groupId>org.apache.tomcat</groupId>
|
||||
<artifactId>annotations-api</artifactId>
|
||||
<version>6.0.53</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-bom</artifactId>
|
||||
<version>${netty.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.amazonaws</groupId>
|
||||
<artifactId>aws-java-sdk-bom</artifactId>
|
||||
<version>${aws.sdk.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>software.amazon.awssdk</groupId>
|
||||
<artifactId>bom</artifactId>
|
||||
<version>${aws.sdk2.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.cloud</groupId>
|
||||
<artifactId>libraries-bom</artifactId>
|
||||
<version>${google-cloud-libraries.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.github.resilience4j</groupId>
|
||||
<artifactId>resilience4j-bom</artifactId>
|
||||
<version>${resilience4j.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.micrometer</groupId>
|
||||
<artifactId>micrometer-bom</artifactId>
|
||||
<version>${micrometer.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.projectreactor</groupId>
|
||||
<artifactId>reactor-bom</artifactId>
|
||||
<version>2020.0.24</version> <!-- 3.4.x, see https://github.com/reactor/reactor#bom-versioning-scheme -->
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-bom</artifactId>
|
||||
<version>${kotlin.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.eatthepath</groupId>
|
||||
<artifactId>pushy</artifactId>
|
||||
<version>${pushy.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.eatthepath</groupId>
|
||||
<artifactId>pushy-dropwizard-metrics-listener</artifactId>
|
||||
<version>${pushy.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.protobuf</groupId>
|
||||
<artifactId>protobuf-java</artifactId>
|
||||
<version>${protobuf.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.googlecode.libphonenumber</groupId>
|
||||
<artifactId>libphonenumber</artifactId>
|
||||
<version>${libphonenumber.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.vdurmont</groupId>
|
||||
<artifactId>semver4j</artifactId>
|
||||
<version>${semver4j.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-io</groupId>
|
||||
<artifactId>commons-io</artifactId>
|
||||
<version>${commons-io.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.lettuce</groupId>
|
||||
<artifactId>lettuce-core</artifactId>
|
||||
<version>${lettuce.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.vavr</groupId>
|
||||
<artifactId>vavr</artifactId>
|
||||
<version>${vavr.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.xml.bind</groupId>
|
||||
<artifactId>jaxb-api</artifactId>
|
||||
<version>${jaxb.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.logstash.logback</groupId>
|
||||
<artifactId>logstash-logback-encoder</artifactId>
|
||||
<version>${logstash.logback.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-csv</artifactId>
|
||||
<version>${commons-csv.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.coursera</groupId>
|
||||
<artifactId>dropwizard-metrics-datadog</artifactId>
|
||||
<version>${dropwizard-metrics-datadog.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.glassfish.jaxb</groupId>
|
||||
<artifactId>jaxb-runtime</artifactId>
|
||||
<version>${jaxb.version}</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-core</artifactId>
|
||||
<version>${mockito.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-inline</artifactId>
|
||||
<version>${mockito.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.opentest4j</groupId>
|
||||
<artifactId>opentest4j</artifactId>
|
||||
<version>${opentest4j.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
<version>${slf4j.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-nop</artifactId>
|
||||
<version>${slf4j.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>redis.clients</groupId>
|
||||
<artifactId>jedis</artifactId>
|
||||
<version>${jedis.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-logging</groupId>
|
||||
<artifactId>commons-logging</artifactId>
|
||||
<version>1.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.ow2.asm</groupId>
|
||||
<artifactId>asm</artifactId>
|
||||
<version>9.2</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.stripe</groupId>
|
||||
<artifactId>stripe-java</artifactId>
|
||||
<version>${stripe.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.braintreepayments.gateway</groupId>
|
||||
<artifactId>braintree-java</artifactId>
|
||||
<version>${braintree.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
<version>${gson.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.signal</groupId>
|
||||
<artifactId>embedded-redis</artifactId>
|
||||
<version>0.8.3</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.signal</groupId>
|
||||
<artifactId>libsignal-server</artifactId>
|
||||
<version>0.22.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-bom</artifactId>
|
||||
<version>2.17.1</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.hamcrest</groupId>
|
||||
<artifactId>hamcrest-all</artifactId>
|
||||
<version>1.3</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.tomakehurst</groupId>
|
||||
<artifactId>wiremock-jre8</artifactId>
|
||||
<version>2.35.0</version>
|
||||
<scope>test</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.hamcrest</groupId>
|
||||
<artifactId>hamcrest-core</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>javax.xml.bind</groupId>
|
||||
<artifactId>jaxb-api</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-core</artifactId>
|
||||
<version>${mockito.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.assertj</groupId>
|
||||
<artifactId>assertj-core</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-api</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit-pioneer</groupId>
|
||||
<artifactId>junit-pioneer</artifactId>
|
||||
<version>1.9.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>include-spam-filter</id>
|
||||
<activation>
|
||||
<file>
|
||||
<exists>spam-filter/pom.xml</exists>
|
||||
</file>
|
||||
</activation>
|
||||
<modules>
|
||||
<module>spam-filter</module>
|
||||
</modules>
|
||||
</profile>
|
||||
|
||||
<profile>
|
||||
<id>exclude-spam-filter</id>
|
||||
<activation>
|
||||
<file>
|
||||
<missing>spam-filter/pom.xml</missing>
|
||||
</file>
|
||||
</activation>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
<build>
|
||||
<extensions>
|
||||
<extension>
|
||||
<groupId>kr.motd.maven</groupId>
|
||||
<artifactId>os-maven-plugin</artifactId>
|
||||
<version>1.7.0</version>
|
||||
</extension>
|
||||
</extensions>
|
||||
<plugins>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.xolstice.maven.plugins</groupId>
|
||||
<artifactId>protobuf-maven-plugin</artifactId>
|
||||
<version>0.6.1</version>
|
||||
<configuration>
|
||||
<checkStaleness>false</checkStaleness>
|
||||
<protocArtifact>com.google.protobuf:protoc:3.21.1:exe:${os.detected.classifier}</protocArtifact>
|
||||
<pluginId>grpc-java</pluginId>
|
||||
<pluginArtifact>io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier}</pluginArtifact>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>compile</goal>
|
||||
<goal>compile-custom</goal>
|
||||
<goal>test-compile</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.8.1</version>
|
||||
<configuration>
|
||||
<release>17</release>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>3.2.0</version>
|
||||
<configuration>
|
||||
<archive>
|
||||
<manifest>
|
||||
<addDefaultImplementationEntries>true</addDefaultImplementationEntries>
|
||||
</manifest>
|
||||
</archive>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-dependency-plugin</artifactId>
|
||||
<version>3.1.2</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>copy</id>
|
||||
<phase>test-compile</phase>
|
||||
<goals>
|
||||
<goal>copy-dependencies</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<includeScope>test</includeScope>
|
||||
<includeTypes>so,dll,dylib</includeTypes>
|
||||
<outputDirectory>${project.build.directory}/lib</outputDirectory>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>3.0.0-M5</version>
|
||||
<configuration>
|
||||
<systemProperties>
|
||||
<property>
|
||||
<name>sqlite4java.library.path</name>
|
||||
<value>${project.build.directory}/lib</value>
|
||||
</property>
|
||||
</systemProperties>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-enforcer-plugin</artifactId>
|
||||
<version>3.0.0-M3</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>enforce</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<rules>
|
||||
<dependencyConvergence/>
|
||||
<requireMavenVersion>
|
||||
<version>3.8.6</version>
|
||||
</requireMavenVersion>
|
||||
</rules>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-install-plugin</artifactId>
|
||||
<version>3.0.0-M1</version>
|
||||
<configuration>
|
||||
<skip>true</skip>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-deploy-plugin</artifactId>
|
||||
<version>3.0.0-M1</version>
|
||||
<configuration>
|
||||
<skip>true</skip>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
@ -1,20 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>TextSecureServer</artifactId>
|
||||
<groupId>org.whispersystems.textsecure</groupId>
|
||||
<version>JGITVER</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>redis-dispatch</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@ -1,11 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2020 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
package org.whispersystems.dispatch;
|
||||
|
||||
public interface DispatchChannel {
|
||||
void onDispatchMessage(String channel, byte[] message);
|
||||
void onDispatchSubscribed(String channel);
|
||||
void onDispatchUnsubscribed(String channel);
|
||||
}
|
||||
@ -1,157 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2020 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
package org.whispersystems.dispatch;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.whispersystems.dispatch.io.RedisPubSubConnectionFactory;
|
||||
import org.whispersystems.dispatch.redis.PubSubConnection;
|
||||
import org.whispersystems.dispatch.redis.PubSubReply;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
|
||||
public class DispatchManager extends Thread {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(DispatchManager.class);
|
||||
private final Executor executor = Executors.newCachedThreadPool();
|
||||
private final Map<String, DispatchChannel> subscriptions = new ConcurrentHashMap<>();
|
||||
|
||||
private final Optional<DispatchChannel> deadLetterChannel;
|
||||
private final RedisPubSubConnectionFactory redisPubSubConnectionFactory;
|
||||
|
||||
private PubSubConnection pubSubConnection;
|
||||
private volatile boolean running;
|
||||
|
||||
public DispatchManager(RedisPubSubConnectionFactory redisPubSubConnectionFactory,
|
||||
Optional<DispatchChannel> deadLetterChannel)
|
||||
{
|
||||
this.redisPubSubConnectionFactory = redisPubSubConnectionFactory;
|
||||
this.deadLetterChannel = deadLetterChannel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
this.pubSubConnection = redisPubSubConnectionFactory.connect();
|
||||
this.running = true;
|
||||
super.start();
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
this.running = false;
|
||||
this.pubSubConnection.close();
|
||||
}
|
||||
|
||||
public synchronized void subscribe(String name, DispatchChannel dispatchChannel) {
|
||||
Optional<DispatchChannel> previous = Optional.ofNullable(subscriptions.get(name));
|
||||
subscriptions.put(name, dispatchChannel);
|
||||
|
||||
try {
|
||||
pubSubConnection.subscribe(name);
|
||||
} catch (IOException e) {
|
||||
logger.warn("Subscription error", e);
|
||||
}
|
||||
|
||||
previous.ifPresent(channel -> dispatchUnsubscription(name, channel));
|
||||
}
|
||||
|
||||
public synchronized void unsubscribe(String name, DispatchChannel channel) {
|
||||
Optional<DispatchChannel> subscription = Optional.ofNullable(subscriptions.get(name));
|
||||
|
||||
if (subscription.isPresent() && subscription.get() == channel) {
|
||||
subscriptions.remove(name);
|
||||
|
||||
try {
|
||||
pubSubConnection.unsubscribe(name);
|
||||
} catch (IOException e) {
|
||||
logger.warn("Unsubscribe error", e);
|
||||
}
|
||||
|
||||
dispatchUnsubscription(name, subscription.get());
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasSubscription(String name) {
|
||||
return subscriptions.containsKey(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
while (running) {
|
||||
try {
|
||||
PubSubReply reply = pubSubConnection.read();
|
||||
|
||||
switch (reply.getType()) {
|
||||
case UNSUBSCRIBE: break;
|
||||
case SUBSCRIBE: dispatchSubscribe(reply); break;
|
||||
case MESSAGE: dispatchMessage(reply); break;
|
||||
default: throw new AssertionError("Unknown pubsub reply type! " + reply.getType());
|
||||
}
|
||||
} catch (IOException e) {
|
||||
logger.warn("***** PubSub Connection Error *****", e);
|
||||
if (running) {
|
||||
this.pubSubConnection.close();
|
||||
this.pubSubConnection = redisPubSubConnectionFactory.connect();
|
||||
resubscribeAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
logger.warn("DispatchManager Shutting Down...");
|
||||
}
|
||||
|
||||
private void dispatchSubscribe(final PubSubReply reply) {
|
||||
Optional<DispatchChannel> subscription = Optional.ofNullable(subscriptions.get(reply.getChannel()));
|
||||
|
||||
if (subscription.isPresent()) {
|
||||
dispatchSubscription(reply.getChannel(), subscription.get());
|
||||
} else {
|
||||
logger.info("Received subscribe event for non-existing channel: " + reply.getChannel());
|
||||
}
|
||||
}
|
||||
|
||||
private void dispatchMessage(PubSubReply reply) {
|
||||
Optional<DispatchChannel> subscription = Optional.ofNullable(subscriptions.get(reply.getChannel()));
|
||||
|
||||
if (subscription.isPresent()) {
|
||||
dispatchMessage(reply.getChannel(), subscription.get(), reply.getContent().get());
|
||||
} else if (deadLetterChannel.isPresent()) {
|
||||
dispatchMessage(reply.getChannel(), deadLetterChannel.get(), reply.getContent().get());
|
||||
} else {
|
||||
logger.warn("Received message for non-existing channel, with no dead letter handler: " + reply.getChannel());
|
||||
}
|
||||
}
|
||||
|
||||
private void resubscribeAll() {
|
||||
new Thread(() -> {
|
||||
synchronized (DispatchManager.this) {
|
||||
try {
|
||||
for (String name : subscriptions.keySet()) {
|
||||
pubSubConnection.subscribe(name);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
logger.warn("***** RESUBSCRIPTION ERROR *****", e);
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
private void dispatchMessage(final String name, final DispatchChannel channel, final byte[] message) {
|
||||
executor.execute(() -> channel.onDispatchMessage(name, message));
|
||||
}
|
||||
|
||||
private void dispatchSubscription(final String name, final DispatchChannel channel) {
|
||||
executor.execute(() -> channel.onDispatchSubscribed(name));
|
||||
}
|
||||
|
||||
private void dispatchUnsubscription(final String name, final DispatchChannel channel) {
|
||||
executor.execute(() -> channel.onDispatchUnsubscribed(name));
|
||||
}
|
||||
}
|
||||
@ -1,68 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2020 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
package org.whispersystems.dispatch.io;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
public class RedisInputStream {
|
||||
|
||||
private static final byte CR = 0x0D;
|
||||
private static final byte LF = 0x0A;
|
||||
|
||||
private final InputStream inputStream;
|
||||
|
||||
public RedisInputStream(InputStream inputStream) {
|
||||
this.inputStream = inputStream;
|
||||
}
|
||||
|
||||
public String readLine() throws IOException {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
|
||||
boolean foundCr = false;
|
||||
|
||||
while (true) {
|
||||
int character = inputStream.read();
|
||||
|
||||
if (character == -1) {
|
||||
throw new IOException("Stream closed!");
|
||||
}
|
||||
|
||||
baos.write(character);
|
||||
|
||||
if (foundCr && character == LF) break;
|
||||
else if (character == CR) foundCr = true;
|
||||
else if (foundCr) foundCr = false;
|
||||
}
|
||||
|
||||
byte[] data = baos.toByteArray();
|
||||
return new String(data, 0, data.length-2);
|
||||
}
|
||||
|
||||
public byte[] readFully(int size) throws IOException {
|
||||
byte[] result = new byte[size];
|
||||
int offset = 0;
|
||||
int remaining = result.length;
|
||||
|
||||
while (remaining > 0) {
|
||||
int read = inputStream.read(result, offset, remaining);
|
||||
|
||||
if (read < 0) {
|
||||
throw new IOException("Stream closed!");
|
||||
}
|
||||
|
||||
offset += read;
|
||||
remaining -= read;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
inputStream.close();
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,13 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2020 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
package org.whispersystems.dispatch.io;
|
||||
|
||||
import org.whispersystems.dispatch.redis.PubSubConnection;
|
||||
|
||||
public interface RedisPubSubConnectionFactory {
|
||||
|
||||
PubSubConnection connect();
|
||||
|
||||
}
|
||||
@ -1,123 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2020 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
package org.whispersystems.dispatch.redis;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.whispersystems.dispatch.io.RedisInputStream;
|
||||
import org.whispersystems.dispatch.redis.protocol.ArrayReplyHeader;
|
||||
import org.whispersystems.dispatch.redis.protocol.IntReply;
|
||||
import org.whispersystems.dispatch.redis.protocol.StringReplyHeader;
|
||||
import org.whispersystems.dispatch.util.Util;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.net.Socket;
|
||||
import java.util.Arrays;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
public class PubSubConnection {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(PubSubConnection.class);
|
||||
|
||||
private static final byte[] UNSUBSCRIBE_TYPE = {'u', 'n', 's', 'u', 'b', 's', 'c', 'r', 'i', 'b', 'e' };
|
||||
private static final byte[] SUBSCRIBE_TYPE = {'s', 'u', 'b', 's', 'c', 'r', 'i', 'b', 'e' };
|
||||
private static final byte[] MESSAGE_TYPE = {'m', 'e', 's', 's', 'a', 'g', 'e' };
|
||||
|
||||
private static final byte[] SUBSCRIBE_COMMAND = {'S', 'U', 'B', 'S', 'C', 'R', 'I', 'B', 'E', ' ' };
|
||||
private static final byte[] UNSUBSCRIBE_COMMAND = {'U', 'N', 'S', 'U', 'B', 'S', 'C', 'R', 'I', 'B', 'E', ' '};
|
||||
private static final byte[] CRLF = {'\r', '\n' };
|
||||
|
||||
private final OutputStream outputStream;
|
||||
private final RedisInputStream inputStream;
|
||||
private final Socket socket;
|
||||
private final AtomicBoolean closed;
|
||||
|
||||
public PubSubConnection(Socket socket) throws IOException {
|
||||
this.socket = socket;
|
||||
this.outputStream = socket.getOutputStream();
|
||||
this.inputStream = new RedisInputStream(new BufferedInputStream(socket.getInputStream()));
|
||||
this.closed = new AtomicBoolean(false);
|
||||
}
|
||||
|
||||
public void subscribe(String channelName) throws IOException {
|
||||
if (closed.get()) throw new IOException("Connection closed!");
|
||||
|
||||
byte[] command = Util.combine(SUBSCRIBE_COMMAND, channelName.getBytes(), CRLF);
|
||||
outputStream.write(command);
|
||||
}
|
||||
|
||||
public void unsubscribe(String channelName) throws IOException {
|
||||
if (closed.get()) throw new IOException("Connection closed!");
|
||||
|
||||
byte[] command = Util.combine(UNSUBSCRIBE_COMMAND, channelName.getBytes(), CRLF);
|
||||
outputStream.write(command);
|
||||
}
|
||||
|
||||
public PubSubReply read() throws IOException {
|
||||
if (closed.get()) throw new IOException("Connection closed!");
|
||||
|
||||
ArrayReplyHeader replyHeader = new ArrayReplyHeader(inputStream.readLine());
|
||||
|
||||
if (replyHeader.getElementCount() != 3) {
|
||||
throw new IOException("Received array reply header with strange count: " + replyHeader.getElementCount());
|
||||
}
|
||||
|
||||
StringReplyHeader replyTypeHeader = new StringReplyHeader(inputStream.readLine());
|
||||
byte[] replyType = inputStream.readFully(replyTypeHeader.getStringLength());
|
||||
inputStream.readLine();
|
||||
|
||||
if (Arrays.equals(SUBSCRIBE_TYPE, replyType)) return readSubscribeReply();
|
||||
else if (Arrays.equals(UNSUBSCRIBE_TYPE, replyType)) return readUnsubscribeReply();
|
||||
else if (Arrays.equals(MESSAGE_TYPE, replyType)) return readMessageReply();
|
||||
else throw new IOException("Unknown reply type: " + new String(replyType));
|
||||
}
|
||||
|
||||
public void close() {
|
||||
try {
|
||||
this.closed.set(true);
|
||||
this.inputStream.close();
|
||||
this.outputStream.close();
|
||||
this.socket.close();
|
||||
} catch (IOException e) {
|
||||
logger.warn("Exception while closing", e);
|
||||
}
|
||||
}
|
||||
|
||||
private PubSubReply readMessageReply() throws IOException {
|
||||
StringReplyHeader channelNameHeader = new StringReplyHeader(inputStream.readLine());
|
||||
byte[] channelName = inputStream.readFully(channelNameHeader.getStringLength());
|
||||
inputStream.readLine();
|
||||
|
||||
StringReplyHeader messageHeader = new StringReplyHeader(inputStream.readLine());
|
||||
byte[] message = inputStream.readFully(messageHeader.getStringLength());
|
||||
inputStream.readLine();
|
||||
|
||||
return new PubSubReply(PubSubReply.Type.MESSAGE, new String(channelName), Optional.of(message));
|
||||
}
|
||||
|
||||
private PubSubReply readUnsubscribeReply() throws IOException {
|
||||
String channelName = readSubscriptionReply();
|
||||
return new PubSubReply(PubSubReply.Type.UNSUBSCRIBE, channelName, Optional.empty());
|
||||
}
|
||||
|
||||
private PubSubReply readSubscribeReply() throws IOException {
|
||||
String channelName = readSubscriptionReply();
|
||||
return new PubSubReply(PubSubReply.Type.SUBSCRIBE, channelName, Optional.empty());
|
||||
}
|
||||
|
||||
private String readSubscriptionReply() throws IOException {
|
||||
StringReplyHeader channelNameHeader = new StringReplyHeader(inputStream.readLine());
|
||||
byte[] channelName = inputStream.readFully(channelNameHeader.getStringLength());
|
||||
inputStream.readLine();
|
||||
|
||||
new IntReply(inputStream.readLine());
|
||||
|
||||
return new String(channelName);
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,40 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2020 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
package org.whispersystems.dispatch.redis;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
|
||||
public class PubSubReply {
|
||||
|
||||
public enum Type {
|
||||
MESSAGE,
|
||||
SUBSCRIBE,
|
||||
UNSUBSCRIBE
|
||||
}
|
||||
|
||||
private final Type type;
|
||||
private final String channel;
|
||||
private final Optional<byte[]> content;
|
||||
|
||||
public PubSubReply(Type type, String channel, Optional<byte[]> content) {
|
||||
this.type = type;
|
||||
this.channel = channel;
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
public Type getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public String getChannel() {
|
||||
return channel;
|
||||
}
|
||||
|
||||
public Optional<byte[]> getContent() {
|
||||
return content;
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,28 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2020 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
package org.whispersystems.dispatch.redis.protocol;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class ArrayReplyHeader {
|
||||
|
||||
private final int elementCount;
|
||||
|
||||
public ArrayReplyHeader(String header) throws IOException {
|
||||
if (header == null || header.length() < 2 || header.charAt(0) != '*') {
|
||||
throw new IOException("Invalid array reply header: " + header);
|
||||
}
|
||||
|
||||
try {
|
||||
this.elementCount = Integer.parseInt(header.substring(1));
|
||||
} catch (NumberFormatException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public int getElementCount() {
|
||||
return elementCount;
|
||||
}
|
||||
}
|
||||
@ -1,28 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2020 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
package org.whispersystems.dispatch.redis.protocol;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class IntReply {
|
||||
|
||||
private final int value;
|
||||
|
||||
public IntReply(String reply) throws IOException {
|
||||
if (reply == null || reply.length() < 2 || reply.charAt(0) != ':') {
|
||||
throw new IOException("Invalid int reply: " + reply);
|
||||
}
|
||||
|
||||
try {
|
||||
this.value = Integer.parseInt(reply.substring(1));
|
||||
} catch (NumberFormatException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public int getValue() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
@ -1,28 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2020 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
package org.whispersystems.dispatch.redis.protocol;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class StringReplyHeader {
|
||||
|
||||
private final int stringLength;
|
||||
|
||||
public StringReplyHeader(String header) throws IOException {
|
||||
if (header == null || header.length() < 2 || header.charAt(0) != '$') {
|
||||
throw new IOException("Invalid string reply header: " + header);
|
||||
}
|
||||
|
||||
try {
|
||||
this.stringLength = Integer.parseInt(header.substring(1));
|
||||
} catch (NumberFormatException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public int getStringLength() {
|
||||
return stringLength;
|
||||
}
|
||||
}
|
||||
@ -1,40 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2020 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
package org.whispersystems.dispatch.util;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public class Util {
|
||||
|
||||
public static byte[] combine(byte[]... elements) {
|
||||
try {
|
||||
int sum = 0;
|
||||
|
||||
for (byte[] element : elements) {
|
||||
sum += element.length;
|
||||
}
|
||||
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream(sum);
|
||||
|
||||
for (byte[] element : elements) {
|
||||
baos.write(element);
|
||||
}
|
||||
|
||||
return baos.toByteArray();
|
||||
} catch (IOException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static void sleep(long millis) {
|
||||
try {
|
||||
Thread.sleep(millis);
|
||||
} catch (InterruptedException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,123 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2020 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
package org.whispersystems.dispatch;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||
import static org.mockito.Mockito.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.timeout;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.stubbing.Answer;
|
||||
import org.whispersystems.dispatch.io.RedisPubSubConnectionFactory;
|
||||
import org.whispersystems.dispatch.redis.PubSubConnection;
|
||||
import org.whispersystems.dispatch.redis.PubSubReply;
|
||||
|
||||
public class DispatchManagerTest {
|
||||
|
||||
private PubSubConnection pubSubConnection;
|
||||
private RedisPubSubConnectionFactory socketFactory;
|
||||
private DispatchManager dispatchManager;
|
||||
private PubSubReplyInputStream pubSubReplyInputStream;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() throws Exception {
|
||||
pubSubConnection = mock(PubSubConnection.class );
|
||||
socketFactory = mock(RedisPubSubConnectionFactory.class);
|
||||
pubSubReplyInputStream = new PubSubReplyInputStream();
|
||||
|
||||
when(socketFactory.connect()).thenReturn(pubSubConnection);
|
||||
when(pubSubConnection.read()).thenAnswer((Answer<PubSubReply>) invocationOnMock -> pubSubReplyInputStream.read());
|
||||
|
||||
dispatchManager = new DispatchManager(socketFactory, Optional.empty());
|
||||
dispatchManager.start();
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void tearDown() {
|
||||
dispatchManager.shutdown();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConnect() {
|
||||
verify(socketFactory).connect();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSubscribe() {
|
||||
DispatchChannel dispatchChannel = mock(DispatchChannel.class);
|
||||
dispatchManager.subscribe("foo", dispatchChannel);
|
||||
pubSubReplyInputStream.write(new PubSubReply(PubSubReply.Type.SUBSCRIBE, "foo", Optional.empty()));
|
||||
|
||||
verify(dispatchChannel, timeout(1000)).onDispatchSubscribed(eq("foo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSubscribeUnsubscribe() {
|
||||
DispatchChannel dispatchChannel = mock(DispatchChannel.class);
|
||||
dispatchManager.subscribe("foo", dispatchChannel);
|
||||
dispatchManager.unsubscribe("foo", dispatchChannel);
|
||||
|
||||
pubSubReplyInputStream.write(new PubSubReply(PubSubReply.Type.SUBSCRIBE, "foo", Optional.empty()));
|
||||
pubSubReplyInputStream.write(new PubSubReply(PubSubReply.Type.UNSUBSCRIBE, "foo", Optional.empty()));
|
||||
|
||||
verify(dispatchChannel, timeout(1000)).onDispatchUnsubscribed(eq("foo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMessages() {
|
||||
DispatchChannel fooChannel = mock(DispatchChannel.class);
|
||||
DispatchChannel barChannel = mock(DispatchChannel.class);
|
||||
|
||||
dispatchManager.subscribe("foo", fooChannel);
|
||||
dispatchManager.subscribe("bar", barChannel);
|
||||
|
||||
pubSubReplyInputStream.write(new PubSubReply(PubSubReply.Type.SUBSCRIBE, "foo", Optional.empty()));
|
||||
pubSubReplyInputStream.write(new PubSubReply(PubSubReply.Type.SUBSCRIBE, "bar", Optional.empty()));
|
||||
|
||||
verify(fooChannel, timeout(1000)).onDispatchSubscribed(eq("foo"));
|
||||
verify(barChannel, timeout(1000)).onDispatchSubscribed(eq("bar"));
|
||||
|
||||
pubSubReplyInputStream.write(new PubSubReply(PubSubReply.Type.MESSAGE, "foo", Optional.of("hello".getBytes())));
|
||||
pubSubReplyInputStream.write(new PubSubReply(PubSubReply.Type.MESSAGE, "bar", Optional.of("there".getBytes())));
|
||||
|
||||
ArgumentCaptor<byte[]> captor = ArgumentCaptor.forClass(byte[].class);
|
||||
verify(fooChannel, timeout(1000)).onDispatchMessage(eq("foo"), captor.capture());
|
||||
|
||||
assertArrayEquals("hello".getBytes(), captor.getValue());
|
||||
|
||||
verify(barChannel, timeout(1000)).onDispatchMessage(eq("bar"), captor.capture());
|
||||
|
||||
assertArrayEquals("there".getBytes(), captor.getValue());
|
||||
}
|
||||
|
||||
private static class PubSubReplyInputStream {
|
||||
|
||||
private final List<PubSubReply> pubSubReplyList = new LinkedList<>();
|
||||
|
||||
public synchronized PubSubReply read() {
|
||||
try {
|
||||
while (pubSubReplyList.isEmpty()) wait();
|
||||
return pubSubReplyList.remove(0);
|
||||
} catch (InterruptedException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void write(PubSubReply pubSubReply) {
|
||||
pubSubReplyList.add(pubSubReply);
|
||||
notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,264 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2020 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
package org.whispersystems.dispatch.redis;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.Socket;
|
||||
import java.security.SecureRandom;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.invocation.InvocationOnMock;
|
||||
import org.mockito.stubbing.Answer;
|
||||
|
||||
class PubSubConnectionTest {
|
||||
|
||||
private static final String REPLY = "*3\r\n" +
|
||||
"$9\r\n" +
|
||||
"subscribe\r\n" +
|
||||
"$5\r\n" +
|
||||
"abcde\r\n" +
|
||||
":1\r\n" +
|
||||
"*3\r\n" +
|
||||
"$9\r\n" +
|
||||
"subscribe\r\n" +
|
||||
"$5\r\n" +
|
||||
"fghij\r\n" +
|
||||
":2\r\n" +
|
||||
"*3\r\n" +
|
||||
"$9\r\n" +
|
||||
"subscribe\r\n" +
|
||||
"$5\r\n" +
|
||||
"klmno\r\n" +
|
||||
":2\r\n" +
|
||||
"*3\r\n" +
|
||||
"$7\r\n" +
|
||||
"message\r\n" +
|
||||
"$5\r\n" +
|
||||
"abcde\r\n" +
|
||||
"$10\r\n" +
|
||||
"1234567890\r\n" +
|
||||
"*3\r\n" +
|
||||
"$7\r\n" +
|
||||
"message\r\n" +
|
||||
"$5\r\n" +
|
||||
"klmno\r\n" +
|
||||
"$10\r\n" +
|
||||
"0987654321\r\n";
|
||||
|
||||
|
||||
@Test
|
||||
void testSubscribe() throws IOException {
|
||||
OutputStream outputStream = mock(OutputStream.class);
|
||||
Socket socket = mock(Socket.class );
|
||||
when(socket.getOutputStream()).thenReturn(outputStream);
|
||||
PubSubConnection connection = new PubSubConnection(socket);
|
||||
|
||||
connection.subscribe("foobar");
|
||||
|
||||
ArgumentCaptor<byte[]> captor = ArgumentCaptor.forClass(byte[].class);
|
||||
verify(outputStream).write(captor.capture());
|
||||
|
||||
assertArrayEquals(captor.getValue(), "SUBSCRIBE foobar\r\n".getBytes());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testUnsubscribe() throws IOException {
|
||||
OutputStream outputStream = mock(OutputStream.class);
|
||||
Socket socket = mock(Socket.class );
|
||||
when(socket.getOutputStream()).thenReturn(outputStream);
|
||||
PubSubConnection connection = new PubSubConnection(socket);
|
||||
|
||||
connection.unsubscribe("bazbar");
|
||||
|
||||
ArgumentCaptor<byte[]> captor = ArgumentCaptor.forClass(byte[].class);
|
||||
verify(outputStream).write(captor.capture());
|
||||
|
||||
assertArrayEquals(captor.getValue(), "UNSUBSCRIBE bazbar\r\n".getBytes());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testTricklyResponse() throws Exception {
|
||||
InputStream inputStream = mockInputStreamFor(new TrickleInputStream(REPLY.getBytes()));
|
||||
OutputStream outputStream = mock(OutputStream.class);
|
||||
Socket socket = mock(Socket.class );
|
||||
when(socket.getOutputStream()).thenReturn(outputStream);
|
||||
when(socket.getInputStream()).thenReturn(inputStream);
|
||||
|
||||
PubSubConnection pubSubConnection = new PubSubConnection(socket);
|
||||
readResponses(pubSubConnection);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFullResponse() throws Exception {
|
||||
InputStream inputStream = mockInputStreamFor(new FullInputStream(REPLY.getBytes()));
|
||||
OutputStream outputStream = mock(OutputStream.class);
|
||||
Socket socket = mock(Socket.class );
|
||||
when(socket.getOutputStream()).thenReturn(outputStream);
|
||||
when(socket.getInputStream()).thenReturn(inputStream);
|
||||
|
||||
PubSubConnection pubSubConnection = new PubSubConnection(socket);
|
||||
readResponses(pubSubConnection);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRandomLengthResponse() throws Exception {
|
||||
InputStream inputStream = mockInputStreamFor(new RandomInputStream(REPLY.getBytes()));
|
||||
OutputStream outputStream = mock(OutputStream.class);
|
||||
Socket socket = mock(Socket.class );
|
||||
when(socket.getOutputStream()).thenReturn(outputStream);
|
||||
when(socket.getInputStream()).thenReturn(inputStream);
|
||||
|
||||
PubSubConnection pubSubConnection = new PubSubConnection(socket);
|
||||
readResponses(pubSubConnection);
|
||||
}
|
||||
|
||||
private InputStream mockInputStreamFor(final MockInputStream stub) throws IOException {
|
||||
InputStream result = mock(InputStream.class);
|
||||
|
||||
when(result.read()).thenAnswer(new Answer<Integer>() {
|
||||
@Override
|
||||
public Integer answer(InvocationOnMock invocationOnMock) throws Throwable {
|
||||
return stub.read();
|
||||
}
|
||||
});
|
||||
|
||||
when(result.read(any(byte[].class))).thenAnswer(new Answer<Integer>() {
|
||||
@Override
|
||||
public Integer answer(InvocationOnMock invocationOnMock) throws Throwable {
|
||||
byte[] buffer = (byte[])invocationOnMock.getArguments()[0];
|
||||
return stub.read(buffer, 0, buffer.length);
|
||||
}
|
||||
});
|
||||
|
||||
when(result.read(any(byte[].class), anyInt(), anyInt())).thenAnswer(new Answer<Integer>() {
|
||||
@Override
|
||||
public Integer answer(InvocationOnMock invocationOnMock) throws Throwable {
|
||||
byte[] buffer = (byte[]) invocationOnMock.getArguments()[0];
|
||||
int offset = (int) invocationOnMock.getArguments()[1];
|
||||
int length = (int) invocationOnMock.getArguments()[2];
|
||||
|
||||
return stub.read(buffer, offset, length);
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private void readResponses(PubSubConnection pubSubConnection) throws Exception {
|
||||
PubSubReply reply = pubSubConnection.read();
|
||||
|
||||
assertEquals(reply.getType(), PubSubReply.Type.SUBSCRIBE);
|
||||
assertEquals(reply.getChannel(), "abcde");
|
||||
assertFalse(reply.getContent().isPresent());
|
||||
|
||||
reply = pubSubConnection.read();
|
||||
|
||||
assertEquals(reply.getType(), PubSubReply.Type.SUBSCRIBE);
|
||||
assertEquals(reply.getChannel(), "fghij");
|
||||
assertFalse(reply.getContent().isPresent());
|
||||
|
||||
reply = pubSubConnection.read();
|
||||
|
||||
assertEquals(reply.getType(), PubSubReply.Type.SUBSCRIBE);
|
||||
assertEquals(reply.getChannel(), "klmno");
|
||||
assertFalse(reply.getContent().isPresent());
|
||||
|
||||
reply = pubSubConnection.read();
|
||||
|
||||
assertEquals(reply.getType(), PubSubReply.Type.MESSAGE);
|
||||
assertEquals(reply.getChannel(), "abcde");
|
||||
assertArrayEquals(reply.getContent().get(), "1234567890".getBytes());
|
||||
|
||||
reply = pubSubConnection.read();
|
||||
|
||||
assertEquals(reply.getType(), PubSubReply.Type.MESSAGE);
|
||||
assertEquals(reply.getChannel(), "klmno");
|
||||
assertArrayEquals(reply.getContent().get(), "0987654321".getBytes());
|
||||
}
|
||||
|
||||
private interface MockInputStream {
|
||||
public int read();
|
||||
public int read(byte[] input, int offset, int length);
|
||||
}
|
||||
|
||||
private static class TrickleInputStream implements MockInputStream {
|
||||
|
||||
private final byte[] data;
|
||||
private int index = 0;
|
||||
|
||||
private TrickleInputStream(byte[] data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public int read() {
|
||||
return data[index++];
|
||||
}
|
||||
|
||||
public int read(byte[] input, int offset, int length) {
|
||||
input[offset] = data[index++];
|
||||
return 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class FullInputStream implements MockInputStream {
|
||||
|
||||
private final byte[] data;
|
||||
private int index = 0;
|
||||
|
||||
private FullInputStream(byte[] data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public int read() {
|
||||
return data[index++];
|
||||
}
|
||||
|
||||
public int read(byte[] input, int offset, int length) {
|
||||
int amount = Math.min(data.length - index, length);
|
||||
System.arraycopy(data, index, input, offset, amount);
|
||||
index += length;
|
||||
|
||||
return amount;
|
||||
}
|
||||
}
|
||||
|
||||
private static class RandomInputStream implements MockInputStream {
|
||||
private final byte[] data;
|
||||
private int index = 0;
|
||||
|
||||
private RandomInputStream(byte[] data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public int read() {
|
||||
return data[index++];
|
||||
}
|
||||
|
||||
public int read(byte[] input, int offset, int length) {
|
||||
int maxCopy = Math.min(data.length - index, length);
|
||||
int randomCopy = new SecureRandom().nextInt(maxCopy) + 1;
|
||||
int copyAmount = Math.min(maxCopy, randomCopy);
|
||||
|
||||
System.arraycopy(data, index, input, offset, copyAmount);
|
||||
index += copyAmount;
|
||||
|
||||
return copyAmount;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,54 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2020 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
package org.whispersystems.dispatch.redis.protocol;
|
||||
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
import java.io.IOException;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class ArrayReplyHeaderTest {
|
||||
|
||||
@Test
|
||||
void testNull() {
|
||||
assertThrows(IOException.class, () -> new ArrayReplyHeader(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testBadPrefix() {
|
||||
assertThrows(IOException.class, () -> new ArrayReplyHeader(":3"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testEmpty() {
|
||||
assertThrows(IOException.class, () -> new ArrayReplyHeader(""));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testTruncated() {
|
||||
assertThrows(IOException.class, () -> new ArrayReplyHeader("*"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testBadNumber() {
|
||||
assertThrows(IOException.class, () -> new ArrayReplyHeader("*ABC"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testValid() throws IOException {
|
||||
assertEquals(4, new ArrayReplyHeader("*4").getElementCount());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@ -1,39 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2020 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
package org.whispersystems.dispatch.redis.protocol;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
import java.io.IOException;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class IntReplyHeaderTest {
|
||||
|
||||
@Test
|
||||
void testNull() {
|
||||
assertThrows(IOException.class, () -> new IntReply(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testEmpty() {
|
||||
assertThrows(IOException.class, () -> new IntReply(""));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testBadNumber() {
|
||||
assertThrows(IOException.class, () -> new IntReply(":A"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testBadFormat() {
|
||||
assertThrows(IOException.class, () -> new IntReply("*"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testValid() throws IOException {
|
||||
assertEquals(23, new IntReply(":23").getValue());
|
||||
}
|
||||
}
|
||||
@ -1,35 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2020 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
package org.whispersystems.dispatch.redis.protocol;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
import java.io.IOException;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class StringReplyHeaderTest {
|
||||
|
||||
@Test
|
||||
void testNull() {
|
||||
assertThrows(IOException.class, () -> new StringReplyHeader(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testBadNumber() {
|
||||
assertThrows(IOException.class, () -> new StringReplyHeader("$100A"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testBadPrefix() {
|
||||
assertThrows(IOException.class, () -> new StringReplyHeader("*"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testValid() throws IOException {
|
||||
assertEquals(1000, new StringReplyHeader("$1000").getStringLength());
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,25 +0,0 @@
|
||||
<assembly xmlns="http://maven.apache.org/ASSEMBLY/2.1.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.1.0 http://maven.apache.org/xsd/assembly-2.1.0.xsd">
|
||||
<id>bin</id>
|
||||
<includeBaseDirectory>false</includeBaseDirectory>
|
||||
<formats>
|
||||
<format>tar.gz</format>
|
||||
</formats>
|
||||
<fileSets>
|
||||
<fileSet>
|
||||
<directory>${project.basedir}/config</directory>
|
||||
<outputDirectory>/config</outputDirectory>
|
||||
<includes>
|
||||
<include>*</include>
|
||||
</includes>
|
||||
</fileSet>
|
||||
<fileSet>
|
||||
<directory>${project.build.directory}</directory>
|
||||
<outputDirectory>/</outputDirectory>
|
||||
<includes>
|
||||
<include>${parent.artifactId}-${project.version}.jar</include>
|
||||
</includes>
|
||||
</fileSet>
|
||||
</fileSets>
|
||||
</assembly>
|
||||
@ -1,407 +0,0 @@
|
||||
# Example, relatively minimal, configuration that passes validation (see `io.dropwizard.cli.CheckCommand`)
|
||||
#
|
||||
# `unset` values will need to be set to work properly.
|
||||
# Most other values are technically valid for a local/demonstration environment, but are probably not production-ready.
|
||||
|
||||
adminEventLoggingConfiguration:
|
||||
credentials: |
|
||||
Some credentials text
|
||||
blah blah blah
|
||||
projectId: some-project-id
|
||||
logName: some-log-name
|
||||
|
||||
stripe:
|
||||
apiKey: unset
|
||||
idempotencyKeyGenerator: abcdefg12345678= # base64 for creating request idempotency hash
|
||||
boostDescription: >
|
||||
Example
|
||||
supportedCurrencies:
|
||||
- xts
|
||||
# - ...
|
||||
# - Nth supported currency
|
||||
|
||||
|
||||
braintree:
|
||||
merchantId: unset
|
||||
publicKey: unset
|
||||
privateKey: unset
|
||||
environment: unset
|
||||
graphqlUrl: unset
|
||||
merchantAccounts:
|
||||
# ISO 4217 currency code and its corresponding sub-merchant account
|
||||
'xts': unset
|
||||
supportedCurrencies:
|
||||
- xts
|
||||
# - ...
|
||||
# - Nth supported currency
|
||||
|
||||
dynamoDbClientConfiguration:
|
||||
region: us-west-2 # AWS Region
|
||||
|
||||
dynamoDbTables:
|
||||
accounts:
|
||||
tableName: Example_Accounts
|
||||
phoneNumberTableName: Example_Accounts_PhoneNumbers
|
||||
phoneNumberIdentifierTableName: Example_Accounts_PhoneNumberIdentifiers
|
||||
usernamesTableName: Example_Accounts_Usernames
|
||||
scanPageSize: 100
|
||||
deletedAccounts:
|
||||
tableName: Example_DeletedAccounts
|
||||
needsReconciliationIndexName: NeedsReconciliation
|
||||
deletedAccountsLock:
|
||||
tableName: Example_DeletedAccountsLock
|
||||
issuedReceipts:
|
||||
tableName: Example_IssuedReceipts
|
||||
expiration: P30D # Duration of time until rows expire
|
||||
generator: abcdefg12345678= # random base64-encoded binary sequence
|
||||
keys:
|
||||
tableName: Example_Keys
|
||||
messages:
|
||||
tableName: Example_Messages
|
||||
expiration: P30D # Duration of time until rows expire
|
||||
pendingAccounts:
|
||||
tableName: Example_PendingAccounts
|
||||
pendingDevices:
|
||||
tableName: Example_PendingDevices
|
||||
phoneNumberIdentifiers:
|
||||
tableName: Example_PhoneNumberIdentifiers
|
||||
profiles:
|
||||
tableName: Example_Profiles
|
||||
pushChallenge:
|
||||
tableName: Example_PushChallenge
|
||||
redeemedReceipts:
|
||||
tableName: Example_RedeemedReceipts
|
||||
expiration: P30D # Duration of time until rows expire
|
||||
remoteConfig:
|
||||
tableName: Example_RemoteConfig
|
||||
reportMessage:
|
||||
tableName: Example_ReportMessage
|
||||
reservedUsernames:
|
||||
tableName: Example_ReservedUsernames
|
||||
subscriptions:
|
||||
tableName: Example_Subscriptions
|
||||
registrationRecovery:
|
||||
tableName: Example_RegistrationRecovery
|
||||
expiration: P300D # Duration of time until rows expire
|
||||
|
||||
cacheCluster: # Redis server configuration for cache cluster
|
||||
configurationUri: redis://redis.example.com:6379/
|
||||
|
||||
clientPresenceCluster: # Redis server configuration for client presence cluster
|
||||
configurationUri: redis://redis.example.com:6379/
|
||||
|
||||
pubsub: # Redis server configuration for pubsub cluster
|
||||
url: redis://redis.example.com:6379/
|
||||
replicaUrls:
|
||||
- redis://redis.example.com:6379/
|
||||
|
||||
pushSchedulerCluster: # Redis server configuration for push scheduler cluster
|
||||
configurationUri: redis://redis.example.com:6379/
|
||||
|
||||
rateLimitersCluster: # Redis server configuration for rate limiters cluster
|
||||
configurationUri: redis://redis.example.com:6379/
|
||||
|
||||
directory:
|
||||
client: # Configuration for interfacing with Contact Discovery Service cluster
|
||||
userAuthenticationTokenSharedSecret: 00000f # hex-encoded secret shared with CDS used to generate auth tokens for Signal users
|
||||
userAuthenticationTokenUserIdSecret: 00000f # hex-encoded secret shared among Signal-Servers to obscure user phone numbers from CDS
|
||||
sqs:
|
||||
accessKey: test # AWS SQS accessKey
|
||||
accessSecret: test # AWS SQS accessSecret
|
||||
queueUrls: # AWS SQS queue urls
|
||||
- https://sqs.example.com/directory.fifo
|
||||
server: # One or more CDS servers
|
||||
- replicationName: example # CDS replication name
|
||||
replicationUrl: cds.example.com # CDS replication endpoint base url
|
||||
replicationPassword: example # CDS replication endpoint password
|
||||
replicationCaCertificates: # CDS replication endpoint TLS certificate trust root
|
||||
- |
|
||||
-----BEGIN CERTIFICATE-----
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
AAAAAAAAAAAAAAAAAAAA
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
directoryV2:
|
||||
client: # Configuration for interfacing with Contact Discovery Service v2 cluster
|
||||
userAuthenticationTokenSharedSecret: abcdefghijklmnopqrstuvwxyz0123456789ABCDEFG= # base64-encoded secret shared with CDS to generate auth tokens for Signal users
|
||||
userIdTokenSharedSecret: bbcdefghijklmnopqrstuvwxyz0123456789ABCDEFG= # base64-encoded secret shared with CDS to generate auth identity tokens for Signal users
|
||||
|
||||
svr2:
|
||||
userAuthenticationTokenSharedSecret: abcdefghijklmnopqrstuvwxyz0123456789ABCDEFG= # base64-encoded secret shared with SVR2 to generate auth tokens for Signal users
|
||||
userIdTokenSharedSecret: bbcdefghijklmnopqrstuvwxyz0123456789ABCDEFG= # base64-encoded secret shared with SVR2 to generate auth identity tokens for Signal users
|
||||
|
||||
messageCache: # Redis server configuration for message store cache
|
||||
persistDelayMinutes: 1
|
||||
cluster:
|
||||
configurationUri: redis://redis.example.com:6379/
|
||||
|
||||
metricsCluster:
|
||||
configurationUri: redis://redis.example.com:6379/
|
||||
|
||||
awsAttachments: # AWS S3 configuration
|
||||
accessKey: test
|
||||
accessSecret: test
|
||||
bucket: aws-attachments
|
||||
region: us-west-2
|
||||
|
||||
gcpAttachments: # GCP Storage configuration
|
||||
domain: example.com
|
||||
email: user@example.cocm
|
||||
maxSizeInBytes: 1024
|
||||
pathPrefix:
|
||||
rsaSigningKey: |
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
AAAAAAAA
|
||||
-----END PRIVATE KEY-----
|
||||
|
||||
accountDatabaseCrawler:
|
||||
chunkSize: 10 # accounts per run
|
||||
chunkIntervalMs: 60000 # time per run
|
||||
|
||||
apn: # Apple Push Notifications configuration
|
||||
sandbox: true
|
||||
bundleId: com.example.textsecuregcm
|
||||
keyId: unset
|
||||
teamId: unset
|
||||
signingKey: |
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
AAAAAAAA
|
||||
-----END PRIVATE KEY-----
|
||||
|
||||
fcm: # FCM configuration
|
||||
credentials: |
|
||||
{ "json": true }
|
||||
|
||||
cdn:
|
||||
accessKey: test # AWS Access Key ID
|
||||
accessSecret: test # AWS Access Secret
|
||||
bucket: cdn # S3 Bucket name
|
||||
region: us-west-2 # AWS region
|
||||
|
||||
datadog:
|
||||
apiKey: unset
|
||||
environment: dev
|
||||
|
||||
unidentifiedDelivery:
|
||||
certificate: ABCD1234
|
||||
privateKey: ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789AAAAAAA
|
||||
expiresDays: 7
|
||||
|
||||
recaptcha:
|
||||
projectPath: projects/example
|
||||
credentialConfigurationJson: "{ }" # service account configuration for backend authentication
|
||||
|
||||
hCaptcha:
|
||||
apiKey: unset
|
||||
|
||||
storageService:
|
||||
uri: storage.example.com
|
||||
userAuthenticationTokenSharedSecret: 00000f
|
||||
storageCaCertificates:
|
||||
- |
|
||||
-----BEGIN CERTIFICATE-----
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
AAAAAAAAAAAAAAAAAAAA
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
backupService:
|
||||
uri: backup.example.com
|
||||
userAuthenticationTokenSharedSecret: 00000f
|
||||
backupCaCertificates:
|
||||
- |
|
||||
-----BEGIN CERTIFICATE-----
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
AAAAAAAAAAAAAAAAAAAA
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
zkConfig:
|
||||
serverPublic: ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
serverSecret: ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzAA==
|
||||
|
||||
appConfig:
|
||||
application: example
|
||||
environment: example
|
||||
configuration: example
|
||||
|
||||
remoteConfig:
|
||||
authorizedTokens:
|
||||
- # 1st authorized token
|
||||
- # 2nd authorized token
|
||||
- # ...
|
||||
- # Nth authorized token
|
||||
globalConfig: # keys and values that are given to clients on GET /v1/config
|
||||
EXAMPLE_KEY: VALUE
|
||||
|
||||
paymentsService:
|
||||
userAuthenticationTokenSharedSecret: 0000000f0000000f0000000f0000000f0000000f0000000f0000000f0000000f # hex-encoded 32-byte secret shared with MobileCoin services used to generate auth tokens for Signal users
|
||||
fixerApiKey: unset
|
||||
coinMarketCapApiKey: unset
|
||||
coinMarketCapCurrencyIds:
|
||||
MOB: 7878
|
||||
paymentCurrencies:
|
||||
# list of symbols for supported currencies
|
||||
- MOB
|
||||
|
||||
artService:
|
||||
userAuthenticationTokenSharedSecret: 0000000f0000000f0000000f0000000f0000000f0000000f0000000f0000000f # hex-encoded 32-byte secret not shared with any external service, but used in ArtController
|
||||
userAuthenticationTokenUserIdSecret: 00000f # hex-encoded secret to obscure user phone numbers from Sticker Creator
|
||||
|
||||
badges:
|
||||
badges:
|
||||
- id: TEST
|
||||
category: other
|
||||
sprites: # exactly 6
|
||||
- sprite-1.png
|
||||
- sprite-2.png
|
||||
- sprite-3.png
|
||||
- sprite-4.png
|
||||
- sprite-5.png
|
||||
- sprite-6.png
|
||||
svg: example.svg
|
||||
svgs:
|
||||
- light: example-light.svg
|
||||
dark: example-dark.svg
|
||||
badgeIdsEnabledForAll:
|
||||
- TEST
|
||||
receiptLevels:
|
||||
'1': TEST
|
||||
|
||||
subscription: # configuration for Stripe subscriptions
|
||||
badgeGracePeriod: P15D
|
||||
levels:
|
||||
500:
|
||||
badge: EXAMPLE
|
||||
prices:
|
||||
# list of ISO 4217 currency codes and amounts for the given badge level
|
||||
xts:
|
||||
amount: '10'
|
||||
processorIds:
|
||||
STRIPE: price_example # stripe Price ID
|
||||
BRAINTREE: plan_example # braintree Plan ID
|
||||
|
||||
oneTimeDonations:
|
||||
boost:
|
||||
level: 1
|
||||
expiration: P90D
|
||||
badge: EXAMPLE
|
||||
gift:
|
||||
level: 10
|
||||
expiration: P90D
|
||||
badge: EXAMPLE
|
||||
currencies:
|
||||
# ISO 4217 currency codes and amounts in those currencies
|
||||
xts:
|
||||
minimum: '0.5'
|
||||
gift: '2'
|
||||
boosts:
|
||||
- '1'
|
||||
- '2'
|
||||
- '4'
|
||||
- '8'
|
||||
- '20'
|
||||
- '40'
|
||||
|
||||
registrationService:
|
||||
host: registration.example.com
|
||||
apiKey: EXAMPLE
|
||||
registrationCaCertificate: | # Registration service TLS certificate trust root
|
||||
-----BEGIN CERTIFICATE-----
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
AAAAAAAAAAAAAAAAAAAA
|
||||
-----END CERTIFICATE-----
|
||||
630
service/pom.xml
630
service/pom.xml
@ -1,630 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>TextSecureServer</artifactId>
|
||||
<groupId>org.whispersystems.textsecure</groupId>
|
||||
<version>JGITVER</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>service</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>jakarta.servlet</groupId>
|
||||
<artifactId>jakarta.servlet-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>jakarta.validation</groupId>
|
||||
<artifactId>jakarta.validation-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>jakarta.ws.rs</groupId>
|
||||
<artifactId>jakarta.ws.rs-api</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.whispersystems.textsecure</groupId>
|
||||
<artifactId>event-logger</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.whispersystems.textsecure</groupId>
|
||||
<artifactId>redis-dispatch</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.whispersystems.textsecure</groupId>
|
||||
<artifactId>websocket-resources</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.signal</groupId>
|
||||
<artifactId>libsignal-server</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.dropwizard</groupId>
|
||||
<artifactId>dropwizard-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.dropwizard</groupId>
|
||||
<artifactId>dropwizard-auth</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.dropwizard</groupId>
|
||||
<artifactId>dropwizard-client</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.dropwizard</groupId>
|
||||
<artifactId>dropwizard-db</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.dropwizard</groupId>
|
||||
<artifactId>dropwizard-logging</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.dropwizard</groupId>
|
||||
<artifactId>dropwizard-metrics</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.dropwizard</groupId>
|
||||
<artifactId>dropwizard-util</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.dropwizard</groupId>
|
||||
<artifactId>dropwizard-servlets</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.dropwizard</groupId>
|
||||
<artifactId>dropwizard-lifecycle</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.dropwizard</groupId>
|
||||
<artifactId>dropwizard-jersey</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.dropwizard</groupId>
|
||||
<artifactId>dropwizard-jetty</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.dropwizard</groupId>
|
||||
<artifactId>dropwizard-validation</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.dropwizard</groupId>
|
||||
<artifactId>dropwizard-migrations</artifactId>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-access</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.logstash.logback</groupId>
|
||||
<artifactId>logstash-logback-encoder</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.dropwizard.metrics</groupId>
|
||||
<artifactId>metrics-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.dropwizard.metrics</groupId>
|
||||
<artifactId>metrics-healthchecks</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.dropwizard.metrics</groupId>
|
||||
<artifactId>metrics-annotation</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.glassfish.jersey.core</groupId>
|
||||
<artifactId>jersey-common</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.glassfish.jersey.core</groupId>
|
||||
<artifactId>jersey-server</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.glassfish.jersey.core</groupId>
|
||||
<artifactId>jersey-client</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.glassfish.jaxb</groupId>
|
||||
<artifactId>jaxb-runtime</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.dropwizard</groupId>
|
||||
<artifactId>dropwizard-testing</artifactId>
|
||||
<scope>test</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.websocket</groupId>
|
||||
<artifactId>websocket-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-servlets</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-csv</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.google.firebase</groupId>
|
||||
<artifactId>firebase-admin</artifactId>
|
||||
<version>9.1.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.google.code.findbugs</groupId>
|
||||
<artifactId>jsr305</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.github.resilience4j</groupId>
|
||||
<artifactId>resilience4j-circuitbreaker</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.github.resilience4j</groupId>
|
||||
<artifactId>resilience4j-retry</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.github.resilience4j</groupId>
|
||||
<artifactId>resilience4j-reactor</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.grpc</groupId>
|
||||
<artifactId>grpc-netty-shaded</artifactId>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.grpc</groupId>
|
||||
<artifactId>grpc-protobuf</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.grpc</groupId>
|
||||
<artifactId>grpc-stub</artifactId>
|
||||
</dependency>
|
||||
<!-- Needed for gRPC with Java 9+ -->
|
||||
<dependency>
|
||||
<groupId>org.apache.tomcat</groupId>
|
||||
<artifactId>annotations-api</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.micrometer</groupId>
|
||||
<artifactId>micrometer-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.micrometer</groupId>
|
||||
<artifactId>micrometer-registry-datadog</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.coursera</groupId>
|
||||
<artifactId>dropwizard-metrics-datadog</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-annotations</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.dataformat</groupId>
|
||||
<artifactId>jackson-dataformat-yaml</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.datatype</groupId>
|
||||
<artifactId>jackson-datatype-jsr310</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.jaxrs</groupId>
|
||||
<artifactId>jackson-jaxrs-json-provider</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>software.amazon.awssdk</groupId>
|
||||
<artifactId>sts</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>software.amazon.awssdk</groupId>
|
||||
<artifactId>s3</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>software.amazon.awssdk</groupId>
|
||||
<artifactId>sqs</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>software.amazon.awssdk</groupId>
|
||||
<artifactId>dynamodb</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>software.amazon.awssdk</groupId>
|
||||
<artifactId>appconfig</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>software.amazon.awssdk</groupId>
|
||||
<artifactId>appconfigdata</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.amazonaws</groupId>
|
||||
<artifactId>aws-java-sdk-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.amazonaws</groupId>
|
||||
<artifactId>dynamodb-lock-client</artifactId>
|
||||
<version>1.1.0</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>commons-logging</groupId>
|
||||
<artifactId>commons-logging</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>redis.clients</groupId>
|
||||
<artifactId>jedis</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.lettuce</groupId>
|
||||
<artifactId>lettuce-core</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.eatthepath</groupId>
|
||||
<artifactId>pushy</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.eatthepath</groupId>
|
||||
<artifactId>pushy-dropwizard-metrics-listener</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.vdurmont</groupId>
|
||||
<artifactId>semver4j</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.google.protobuf</groupId>
|
||||
<artifactId>protobuf-java</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.googlecode.libphonenumber</groupId>
|
||||
<artifactId>libphonenumber</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>net.sourceforge.argparse4j</groupId>
|
||||
<artifactId>argparse4j</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.glassfish.jersey.test-framework</groupId>
|
||||
<artifactId>jersey-test-framework-core</artifactId>
|
||||
<scope>test</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.glassfish.jersey.test-framework.providers</groupId>
|
||||
<artifactId>jersey-test-framework-provider-grizzly2</artifactId>
|
||||
<scope>test</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.almworks.sqlite4java</groupId>
|
||||
<artifactId>sqlite4java</artifactId>
|
||||
<version>1.0.392</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.projectreactor</groupId>
|
||||
<artifactId>reactor-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.vavr</groupId>
|
||||
<artifactId>vavr</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-params</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.projectreactor</groupId>
|
||||
<artifactId>reactor-test</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.signal</groupId>
|
||||
<artifactId>embedded-redis</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.uuid</groupId>
|
||||
<artifactId>java-uuid-generator</artifactId>
|
||||
<version>4.0.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.amazonaws</groupId>
|
||||
<artifactId>DynamoDBLocal</artifactId>
|
||||
<version>1.21.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.github.ganadist.sqlite4java</groupId>
|
||||
<artifactId>libsqlite4java-osx-aarch64</artifactId>
|
||||
<version>1.0.392</version>
|
||||
<type>dylib</type>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.google.cloud</groupId>
|
||||
<artifactId>google-cloud-recaptchaenterprise</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.stripe</groupId>
|
||||
<artifactId>stripe-java</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.braintreepayments.gateway</groupId>
|
||||
<artifactId>braintree-java</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.apollographql.apollo3</groupId>
|
||||
<artifactId>apollo-api-jvm</artifactId>
|
||||
<version>3.7.1</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>exclude-spam-filter</id>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.2.4</version>
|
||||
<configuration>
|
||||
<createDependencyReducedPom>true</createDependencyReducedPom>
|
||||
<filters>
|
||||
<filter>
|
||||
<artifact>*:*</artifact>
|
||||
<excludes>
|
||||
<exclude>META-INF/*.SF</exclude>
|
||||
<exclude>META-INF/*.DSA</exclude>
|
||||
<exclude>META-INF/*.RSA</exclude>
|
||||
</excludes>
|
||||
</filter>
|
||||
</filters>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<transformers>
|
||||
<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
|
||||
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
|
||||
<mainClass>org.whispersystems.textsecuregcm.WhisperServerService</mainClass>
|
||||
</transformer>
|
||||
</transformers>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
<version>3.3.0</version>
|
||||
<configuration>
|
||||
<descriptors>
|
||||
<descriptor>assembly.xml</descriptor>
|
||||
</descriptors>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>make-assembly</id> <!-- this is used for inheritance merges -->
|
||||
<phase>package</phase> <!-- bind to the packaging phase -->
|
||||
<goals>
|
||||
<goal>single</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>properties-maven-plugin</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>read-deploy-configuration</id>
|
||||
<phase>deploy</phase>
|
||||
<goals>
|
||||
<goal>read-project-properties</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<files>${project.basedir}/config/deploy.properties</files>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.signal</groupId>
|
||||
<artifactId>s3-upload-maven-plugin</artifactId>
|
||||
<version>1.6-SNAPSHOT</version>
|
||||
<configuration>
|
||||
<source>${project.build.directory}/${project.build.finalName}-bin.tar.gz</source>
|
||||
<bucketName>${deploy.bucketName}</bucketName>
|
||||
<region>${deploy.bucketRegion}</region>
|
||||
<destination>${project.build.finalName}-bin.tar.gz</destination>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>deploy-to-s3</id>
|
||||
<phase>deploy</phase>
|
||||
<goals>
|
||||
<goal>s3-upload</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
<build>
|
||||
<finalName>${project.parent.artifactId}-${project.version}</finalName>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>templating-maven-plugin</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>filter-src</id>
|
||||
<goals>
|
||||
<goal>filter-sources</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>test-jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>exec-maven-plugin</artifactId>
|
||||
<version>3.0.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>check-all-service-config</id>
|
||||
<phase>verify</phase>
|
||||
<goals>
|
||||
<goal>java</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<mainClass>org.whispersystems.textsecuregcm.CheckServiceConfigurations</mainClass>
|
||||
<classpathScope>test</classpathScope>
|
||||
<arguments>
|
||||
<argument>${project.basedir}/config</argument>
|
||||
</arguments>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>com.github.aoudiamoncef</groupId>
|
||||
<artifactId>apollo-client-maven-plugin</artifactId>
|
||||
<version>5.0.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>generate</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<services>
|
||||
<braintree>
|
||||
<compilationUnit>
|
||||
<name>braintree</name>
|
||||
<compilerParams>
|
||||
<schemaPackageName>com.braintree.graphql.client</schemaPackageName>
|
||||
</compilerParams>
|
||||
</compilationUnit>
|
||||
</braintree>
|
||||
</services>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@ -1,9 +0,0 @@
|
||||
# https://graphql.braintreepayments.com/reference/#Mutation--chargePaymentMethod
|
||||
mutation ChargePayPalOneTimePayment($input: ChargePaymentMethodInput!) {
|
||||
chargePaymentMethod(input: $input) {
|
||||
transaction {
|
||||
id,
|
||||
status
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,6 +0,0 @@
|
||||
mutation CreatePayPalBillingAgreement($input: CreatePayPalBillingAgreementInput!) {
|
||||
createPayPalBillingAgreement(input: $input) {
|
||||
approvalUrl,
|
||||
billingAgreementToken
|
||||
}
|
||||
}
|
||||
@ -1,7 +0,0 @@
|
||||
# https://graphql.braintreepayments.com/reference/#Mutation--createPayPalOneTimePayment
|
||||
mutation CreatePayPalOneTimePayment($input: CreatePayPalOneTimePaymentInput!) {
|
||||
createPayPalOneTimePayment(input: $input) {
|
||||
approvalUrl,
|
||||
paymentId
|
||||
}
|
||||
}
|
||||
@ -1,7 +0,0 @@
|
||||
mutation TokenizePayPalBillingAgreement($input: TokenizePayPalBillingAgreementInput!) {
|
||||
tokenizePayPalBillingAgreement(input: $input) {
|
||||
paymentMethod {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,8 +0,0 @@
|
||||
# https://graphql.braintreepayments.com/reference/#Mutation--tokenizePayPalOneTimePayment
|
||||
mutation TokenizePayPalOneTimePayment($input: TokenizePayPalOneTimePaymentInput!) {
|
||||
tokenizePayPalOneTimePayment(input: $input) {
|
||||
paymentMethod {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,7 +0,0 @@
|
||||
mutation VaultPaymentMethod($input: VaultPaymentMethodInput!) {
|
||||
vaultPaymentMethod(input: $input) {
|
||||
paymentMethod {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,15 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2021 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm;
|
||||
|
||||
public class WhisperServerVersion {
|
||||
|
||||
private static final String VERSION = "${project.version}";
|
||||
|
||||
public static String getServerVersion() {
|
||||
return VERSION;
|
||||
}
|
||||
}
|
||||
@ -1,67 +0,0 @@
|
||||
/*
|
||||
* Copyright 2021 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.signal.i18n;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.ResourceBundle.Control;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public class HeaderControlledResourceBundleLookup {
|
||||
|
||||
private static final int MAX_LOCALES = 15;
|
||||
|
||||
private final ResourceBundleFactory resourceBundleFactory;
|
||||
|
||||
public HeaderControlledResourceBundleLookup() {
|
||||
this(ResourceBundle::getBundle);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public HeaderControlledResourceBundleLookup(
|
||||
@Nonnull final ResourceBundleFactory resourceBundleFactory) {
|
||||
this.resourceBundleFactory = Objects.requireNonNull(resourceBundleFactory);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private List<Locale> getAcceptableLocales(final List<Locale> acceptableLanguages) {
|
||||
return acceptableLanguages.stream().limit(MAX_LOCALES).distinct().collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public ResourceBundle getResourceBundle(final String baseName, final List<Locale> acceptableLocales) {
|
||||
final List<Locale> deduplicatedLocales = getAcceptableLocales(acceptableLocales);
|
||||
final Locale desiredLocale = deduplicatedLocales.isEmpty() ? Locale.getDefault() : deduplicatedLocales.get(0);
|
||||
// define a control with a fallback order as specified in the header
|
||||
Control control = new Control() {
|
||||
@Override
|
||||
public List<String> getFormats(final String baseName) {
|
||||
Objects.requireNonNull(baseName);
|
||||
return Control.FORMAT_PROPERTIES;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Locale getFallbackLocale(final String baseName, final Locale locale) {
|
||||
Objects.requireNonNull(baseName);
|
||||
if (locale.equals(Locale.getDefault())) {
|
||||
return null;
|
||||
}
|
||||
final int localeIndex = deduplicatedLocales.indexOf(locale);
|
||||
if (localeIndex < 0 || localeIndex >= deduplicatedLocales.size() - 1) {
|
||||
return Locale.getDefault();
|
||||
}
|
||||
// [0, deduplicatedLocales.size() - 2] is now the possible range for localeIndex
|
||||
return deduplicatedLocales.get(localeIndex + 1);
|
||||
}
|
||||
};
|
||||
|
||||
return resourceBundleFactory.createBundle(baseName, desiredLocale, control);
|
||||
}
|
||||
}
|
||||
@ -1,13 +0,0 @@
|
||||
/*
|
||||
* Copyright 2021 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.signal.i18n;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
public interface ResourceBundleFactory {
|
||||
ResourceBundle createBundle(String baseName, Locale locale, ResourceBundle.Control control);
|
||||
}
|
||||
@ -1,447 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
package org.whispersystems.textsecuregcm;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import io.dropwizard.Configuration;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.validation.Valid;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import org.whispersystems.textsecuregcm.configuration.SpamFilterConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.AccountDatabaseCrawlerConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.AdminEventLoggingConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.ApnConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.AppConfigConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.AwsAttachmentsConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.BadgesConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.BraintreeConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.CdnConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.DatadogConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.DirectoryConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.DirectoryV2Configuration;
|
||||
import org.whispersystems.textsecuregcm.configuration.DynamoDbClientConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.DynamoDbTables;
|
||||
import org.whispersystems.textsecuregcm.configuration.FcmConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.GcpAttachmentsConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.HCaptchaConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.MaxDeviceConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.MessageCacheConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.OneTimeDonationConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.PaymentsServiceConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.ArtServiceConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.RateLimitsConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.RecaptchaConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.RedisClusterConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.RedisConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.RegistrationServiceConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.RemoteConfigConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.ReportMessageConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.SecureBackupServiceConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.SecureStorageServiceConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.SecureValueRecovery2Configuration;
|
||||
import org.whispersystems.textsecuregcm.configuration.StripeConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.SubscriptionConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.TestDeviceConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.UnidentifiedDeliveryConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.ZkConfig;
|
||||
import org.whispersystems.websocket.configuration.WebSocketConfiguration;
|
||||
|
||||
/** @noinspection MismatchedQueryAndUpdateOfCollection, WeakerAccess */
|
||||
public class WhisperServerConfiguration extends Configuration {
|
||||
|
||||
@NotNull
|
||||
@Valid
|
||||
@JsonProperty
|
||||
private AdminEventLoggingConfiguration adminEventLoggingConfiguration;
|
||||
|
||||
@NotNull
|
||||
@Valid
|
||||
@JsonProperty
|
||||
private StripeConfiguration stripe;
|
||||
|
||||
@NotNull
|
||||
@Valid
|
||||
@JsonProperty
|
||||
private BraintreeConfiguration braintree;
|
||||
|
||||
@NotNull
|
||||
@Valid
|
||||
@JsonProperty
|
||||
private DynamoDbClientConfiguration dynamoDbClientConfiguration;
|
||||
|
||||
@NotNull
|
||||
@Valid
|
||||
@JsonProperty
|
||||
private DynamoDbTables dynamoDbTables;
|
||||
|
||||
@NotNull
|
||||
@Valid
|
||||
@JsonProperty
|
||||
private AwsAttachmentsConfiguration awsAttachments;
|
||||
|
||||
@NotNull
|
||||
@Valid
|
||||
@JsonProperty
|
||||
private GcpAttachmentsConfiguration gcpAttachments;
|
||||
|
||||
@NotNull
|
||||
@Valid
|
||||
@JsonProperty
|
||||
private CdnConfiguration cdn;
|
||||
|
||||
@NotNull
|
||||
@Valid
|
||||
@JsonProperty
|
||||
private DatadogConfiguration datadog;
|
||||
|
||||
@NotNull
|
||||
@Valid
|
||||
@JsonProperty
|
||||
private RedisClusterConfiguration cacheCluster;
|
||||
|
||||
@NotNull
|
||||
@Valid
|
||||
@JsonProperty
|
||||
private RedisConfiguration pubsub;
|
||||
|
||||
@NotNull
|
||||
@Valid
|
||||
@JsonProperty
|
||||
private RedisClusterConfiguration metricsCluster;
|
||||
|
||||
@NotNull
|
||||
@Valid
|
||||
@JsonProperty
|
||||
private DirectoryConfiguration directory;
|
||||
|
||||
@NotNull
|
||||
@Valid
|
||||
@JsonProperty
|
||||
private DirectoryV2Configuration directoryV2;
|
||||
|
||||
@NotNull
|
||||
@Valid
|
||||
@JsonProperty
|
||||
private SecureValueRecovery2Configuration svr2;
|
||||
|
||||
@NotNull
|
||||
@Valid
|
||||
@JsonProperty
|
||||
private AccountDatabaseCrawlerConfiguration accountDatabaseCrawler;
|
||||
|
||||
@NotNull
|
||||
@Valid
|
||||
@JsonProperty
|
||||
private RedisClusterConfiguration pushSchedulerCluster;
|
||||
|
||||
@NotNull
|
||||
@Valid
|
||||
@JsonProperty
|
||||
private RedisClusterConfiguration rateLimitersCluster;
|
||||
|
||||
@NotNull
|
||||
@Valid
|
||||
@JsonProperty
|
||||
private MessageCacheConfiguration messageCache;
|
||||
|
||||
@NotNull
|
||||
@Valid
|
||||
@JsonProperty
|
||||
private RedisClusterConfiguration clientPresenceCluster;
|
||||
|
||||
@Valid
|
||||
@NotNull
|
||||
@JsonProperty
|
||||
private List<TestDeviceConfiguration> testDevices = new LinkedList<>();
|
||||
|
||||
@Valid
|
||||
@NotNull
|
||||
@JsonProperty
|
||||
private List<MaxDeviceConfiguration> maxDevices = new LinkedList<>();
|
||||
|
||||
@Valid
|
||||
@NotNull
|
||||
@JsonProperty
|
||||
private RateLimitsConfiguration limits = new RateLimitsConfiguration();
|
||||
|
||||
@Valid
|
||||
@NotNull
|
||||
@JsonProperty
|
||||
private WebSocketConfiguration webSocket = new WebSocketConfiguration();
|
||||
|
||||
@Valid
|
||||
@NotNull
|
||||
@JsonProperty
|
||||
private FcmConfiguration fcm;
|
||||
|
||||
@Valid
|
||||
@NotNull
|
||||
@JsonProperty
|
||||
private ApnConfiguration apn;
|
||||
|
||||
@Valid
|
||||
@NotNull
|
||||
@JsonProperty
|
||||
private UnidentifiedDeliveryConfiguration unidentifiedDelivery;
|
||||
|
||||
@Valid
|
||||
@NotNull
|
||||
@JsonProperty
|
||||
private RecaptchaConfiguration recaptcha;
|
||||
|
||||
@Valid
|
||||
@NotNull
|
||||
@JsonProperty
|
||||
private HCaptchaConfiguration hCaptcha;
|
||||
|
||||
@Valid
|
||||
@NotNull
|
||||
@JsonProperty
|
||||
private SecureStorageServiceConfiguration storageService;
|
||||
|
||||
@Valid
|
||||
@NotNull
|
||||
@JsonProperty
|
||||
private SecureBackupServiceConfiguration backupService;
|
||||
|
||||
@Valid
|
||||
@NotNull
|
||||
@JsonProperty
|
||||
private PaymentsServiceConfiguration paymentsService;
|
||||
|
||||
@Valid
|
||||
@NotNull
|
||||
@JsonProperty
|
||||
private ArtServiceConfiguration artService;
|
||||
|
||||
@Valid
|
||||
@NotNull
|
||||
@JsonProperty
|
||||
private ZkConfig zkConfig;
|
||||
|
||||
@Valid
|
||||
@NotNull
|
||||
@JsonProperty
|
||||
private RemoteConfigConfiguration remoteConfig;
|
||||
|
||||
@Valid
|
||||
@NotNull
|
||||
@JsonProperty
|
||||
private AppConfigConfiguration appConfig;
|
||||
|
||||
@Valid
|
||||
@NotNull
|
||||
@JsonProperty
|
||||
private BadgesConfiguration badges;
|
||||
|
||||
@Valid
|
||||
@JsonProperty
|
||||
@NotNull
|
||||
private SubscriptionConfiguration subscription;
|
||||
|
||||
@Valid
|
||||
@JsonProperty
|
||||
@NotNull
|
||||
private OneTimeDonationConfiguration oneTimeDonations;
|
||||
|
||||
@Valid
|
||||
@NotNull
|
||||
@JsonProperty
|
||||
private ReportMessageConfiguration reportMessage = new ReportMessageConfiguration();
|
||||
|
||||
@Valid
|
||||
@JsonProperty
|
||||
private SpamFilterConfiguration spamFilterConfiguration;
|
||||
|
||||
@Valid
|
||||
@NotNull
|
||||
@JsonProperty
|
||||
private RegistrationServiceConfiguration registrationService;
|
||||
|
||||
public AdminEventLoggingConfiguration getAdminEventLoggingConfiguration() {
|
||||
return adminEventLoggingConfiguration;
|
||||
}
|
||||
|
||||
public StripeConfiguration getStripe() {
|
||||
return stripe;
|
||||
}
|
||||
|
||||
public BraintreeConfiguration getBraintree() {
|
||||
return braintree;
|
||||
}
|
||||
|
||||
public DynamoDbClientConfiguration getDynamoDbClientConfiguration() {
|
||||
return dynamoDbClientConfiguration;
|
||||
}
|
||||
|
||||
public DynamoDbTables getDynamoDbTables() {
|
||||
return dynamoDbTables;
|
||||
}
|
||||
|
||||
public RecaptchaConfiguration getRecaptchaConfiguration() {
|
||||
return recaptcha;
|
||||
}
|
||||
|
||||
public HCaptchaConfiguration getHCaptchaConfiguration() {
|
||||
return hCaptcha;
|
||||
}
|
||||
|
||||
public WebSocketConfiguration getWebSocketConfiguration() {
|
||||
return webSocket;
|
||||
}
|
||||
|
||||
public AwsAttachmentsConfiguration getAwsAttachmentsConfiguration() {
|
||||
return awsAttachments;
|
||||
}
|
||||
|
||||
public GcpAttachmentsConfiguration getGcpAttachmentsConfiguration() {
|
||||
return gcpAttachments;
|
||||
}
|
||||
|
||||
public RedisClusterConfiguration getCacheClusterConfiguration() {
|
||||
return cacheCluster;
|
||||
}
|
||||
|
||||
public RedisConfiguration getPubsubCacheConfiguration() {
|
||||
return pubsub;
|
||||
}
|
||||
|
||||
public RedisClusterConfiguration getMetricsClusterConfiguration() {
|
||||
return metricsCluster;
|
||||
}
|
||||
|
||||
public DirectoryConfiguration getDirectoryConfiguration() {
|
||||
return directory;
|
||||
}
|
||||
|
||||
public SecureValueRecovery2Configuration getSvr2Configuration() {
|
||||
return svr2;
|
||||
}
|
||||
|
||||
public DirectoryV2Configuration getDirectoryV2Configuration() {
|
||||
return directoryV2;
|
||||
}
|
||||
|
||||
public SecureStorageServiceConfiguration getSecureStorageServiceConfiguration() {
|
||||
return storageService;
|
||||
}
|
||||
|
||||
public AccountDatabaseCrawlerConfiguration getAccountDatabaseCrawlerConfiguration() {
|
||||
return accountDatabaseCrawler;
|
||||
}
|
||||
|
||||
public MessageCacheConfiguration getMessageCacheConfiguration() {
|
||||
return messageCache;
|
||||
}
|
||||
|
||||
public RedisClusterConfiguration getClientPresenceClusterConfiguration() {
|
||||
return clientPresenceCluster;
|
||||
}
|
||||
|
||||
public RedisClusterConfiguration getPushSchedulerCluster() {
|
||||
return pushSchedulerCluster;
|
||||
}
|
||||
|
||||
public RedisClusterConfiguration getRateLimitersCluster() {
|
||||
return rateLimitersCluster;
|
||||
}
|
||||
|
||||
public RateLimitsConfiguration getLimitsConfiguration() {
|
||||
return limits;
|
||||
}
|
||||
|
||||
public FcmConfiguration getFcmConfiguration() {
|
||||
return fcm;
|
||||
}
|
||||
|
||||
public ApnConfiguration getApnConfiguration() {
|
||||
return apn;
|
||||
}
|
||||
|
||||
public CdnConfiguration getCdnConfiguration() {
|
||||
return cdn;
|
||||
}
|
||||
|
||||
public DatadogConfiguration getDatadogConfiguration() {
|
||||
return datadog;
|
||||
}
|
||||
|
||||
public UnidentifiedDeliveryConfiguration getDeliveryCertificate() {
|
||||
return unidentifiedDelivery;
|
||||
}
|
||||
|
||||
public Map<String, Integer> getTestDevices() {
|
||||
Map<String, Integer> results = new HashMap<>();
|
||||
|
||||
for (TestDeviceConfiguration testDeviceConfiguration : testDevices) {
|
||||
results.put(testDeviceConfiguration.getNumber(),
|
||||
testDeviceConfiguration.getCode());
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
public Map<String, Integer> getMaxDevices() {
|
||||
Map<String, Integer> results = new HashMap<>();
|
||||
|
||||
for (MaxDeviceConfiguration maxDeviceConfiguration : maxDevices) {
|
||||
results.put(maxDeviceConfiguration.getNumber(),
|
||||
maxDeviceConfiguration.getCount());
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
public SecureBackupServiceConfiguration getSecureBackupServiceConfiguration() {
|
||||
return backupService;
|
||||
}
|
||||
|
||||
public PaymentsServiceConfiguration getPaymentsServiceConfiguration() {
|
||||
return paymentsService;
|
||||
}
|
||||
|
||||
public ArtServiceConfiguration getArtServiceConfiguration() {
|
||||
return artService;
|
||||
}
|
||||
|
||||
public ZkConfig getZkConfig() {
|
||||
return zkConfig;
|
||||
}
|
||||
|
||||
public RemoteConfigConfiguration getRemoteConfigConfiguration() {
|
||||
return remoteConfig;
|
||||
}
|
||||
|
||||
public AppConfigConfiguration getAppConfig() {
|
||||
return appConfig;
|
||||
}
|
||||
|
||||
public BadgesConfiguration getBadges() {
|
||||
return badges;
|
||||
}
|
||||
|
||||
public SubscriptionConfiguration getSubscription() {
|
||||
return subscription;
|
||||
}
|
||||
|
||||
public OneTimeDonationConfiguration getOneTimeDonations() {
|
||||
return oneTimeDonations;
|
||||
}
|
||||
|
||||
public ReportMessageConfiguration getReportMessageConfiguration() {
|
||||
return reportMessage;
|
||||
}
|
||||
|
||||
public SpamFilterConfiguration getSpamFilterConfiguration() {
|
||||
return spamFilterConfiguration;
|
||||
}
|
||||
|
||||
public RegistrationServiceConfiguration getRegistrationServiceConfiguration() {
|
||||
return registrationService;
|
||||
}
|
||||
}
|
||||
@ -1,870 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
package org.whispersystems.textsecuregcm;
|
||||
|
||||
import static com.codahale.metrics.MetricRegistry.name;
|
||||
|
||||
import com.amazonaws.ClientConfiguration;
|
||||
import com.amazonaws.auth.InstanceProfileCredentialsProvider;
|
||||
import com.amazonaws.services.dynamodbv2.AmazonDynamoDB;
|
||||
import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClientBuilder;
|
||||
import com.codahale.metrics.SharedMetricRegistries;
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.PropertyAccessor;
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.google.auth.oauth2.GoogleCredentials;
|
||||
import com.google.cloud.logging.LoggingOptions;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Lists;
|
||||
import io.dropwizard.Application;
|
||||
import io.dropwizard.auth.AuthFilter;
|
||||
import io.dropwizard.auth.PolymorphicAuthDynamicFeature;
|
||||
import io.dropwizard.auth.PolymorphicAuthValueFactoryProvider;
|
||||
import io.dropwizard.auth.basic.BasicCredentialAuthFilter;
|
||||
import io.dropwizard.auth.basic.BasicCredentials;
|
||||
import io.dropwizard.setup.Bootstrap;
|
||||
import io.dropwizard.setup.Environment;
|
||||
import io.lettuce.core.metrics.MicrometerCommandLatencyRecorder;
|
||||
import io.lettuce.core.metrics.MicrometerOptions;
|
||||
import io.lettuce.core.resource.ClientResources;
|
||||
import io.micrometer.core.instrument.Meter.Id;
|
||||
import io.micrometer.core.instrument.Metrics;
|
||||
import io.micrometer.core.instrument.Tags;
|
||||
import io.micrometer.core.instrument.config.MeterFilter;
|
||||
import io.micrometer.core.instrument.distribution.DistributionStatisticConfig;
|
||||
import io.micrometer.datadog.DatadogMeterRegistry;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.net.http.HttpClient;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Clock;
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.ServiceLoader;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import javax.servlet.DispatcherType;
|
||||
import javax.servlet.FilterRegistration;
|
||||
import javax.servlet.ServletRegistration;
|
||||
import org.eclipse.jetty.servlets.CrossOriginFilter;
|
||||
import org.glassfish.jersey.server.ServerProperties;
|
||||
import org.signal.event.AdminEventLogger;
|
||||
import org.signal.event.GoogleCloudAdminEventLogger;
|
||||
import org.signal.i18n.HeaderControlledResourceBundleLookup;
|
||||
import org.signal.libsignal.zkgroup.ServerSecretParams;
|
||||
import org.signal.libsignal.zkgroup.auth.ServerZkAuthOperations;
|
||||
import org.signal.libsignal.zkgroup.profiles.ServerZkProfileOperations;
|
||||
import org.signal.libsignal.zkgroup.receipts.ReceiptCredentialPresentation;
|
||||
import org.signal.libsignal.zkgroup.receipts.ServerZkReceiptOperations;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.whispersystems.dispatch.DispatchManager;
|
||||
import org.whispersystems.textsecuregcm.auth.AccountAuthenticator;
|
||||
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
|
||||
import org.whispersystems.textsecuregcm.auth.CertificateGenerator;
|
||||
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAccountAuthenticator;
|
||||
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAuthenticatedAccount;
|
||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialsGenerator;
|
||||
import org.whispersystems.textsecuregcm.auth.PhoneVerificationTokenManager;
|
||||
import org.whispersystems.textsecuregcm.auth.RegistrationLockVerificationManager;
|
||||
import org.whispersystems.textsecuregcm.auth.TurnTokenGenerator;
|
||||
import org.whispersystems.textsecuregcm.auth.WebsocketRefreshApplicationEventListener;
|
||||
import org.whispersystems.textsecuregcm.badges.ConfiguredProfileBadgeConverter;
|
||||
import org.whispersystems.textsecuregcm.badges.ResourceBundleLevelTranslator;
|
||||
import org.whispersystems.textsecuregcm.captcha.CaptchaChecker;
|
||||
import org.whispersystems.textsecuregcm.captcha.HCaptchaClient;
|
||||
import org.whispersystems.textsecuregcm.captcha.RecaptchaClient;
|
||||
import org.whispersystems.textsecuregcm.configuration.DirectoryServerConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration;
|
||||
import org.whispersystems.textsecuregcm.controllers.AccountController;
|
||||
import org.whispersystems.textsecuregcm.controllers.AccountControllerV2;
|
||||
import org.whispersystems.textsecuregcm.controllers.ArtController;
|
||||
import org.whispersystems.textsecuregcm.controllers.AttachmentControllerV2;
|
||||
import org.whispersystems.textsecuregcm.controllers.AttachmentControllerV3;
|
||||
import org.whispersystems.textsecuregcm.controllers.CertificateController;
|
||||
import org.whispersystems.textsecuregcm.controllers.ChallengeController;
|
||||
import org.whispersystems.textsecuregcm.controllers.DeviceController;
|
||||
import org.whispersystems.textsecuregcm.controllers.DirectoryController;
|
||||
import org.whispersystems.textsecuregcm.controllers.DirectoryV2Controller;
|
||||
import org.whispersystems.textsecuregcm.controllers.DonationController;
|
||||
import org.whispersystems.textsecuregcm.controllers.KeepAliveController;
|
||||
import org.whispersystems.textsecuregcm.controllers.KeysController;
|
||||
import org.whispersystems.textsecuregcm.controllers.MessageController;
|
||||
import org.whispersystems.textsecuregcm.controllers.PaymentsController;
|
||||
import org.whispersystems.textsecuregcm.controllers.ProfileController;
|
||||
import org.whispersystems.textsecuregcm.controllers.ProvisioningController;
|
||||
import org.whispersystems.textsecuregcm.controllers.RegistrationController;
|
||||
import org.whispersystems.textsecuregcm.controllers.RemoteConfigController;
|
||||
import org.whispersystems.textsecuregcm.controllers.SecureBackupController;
|
||||
import org.whispersystems.textsecuregcm.controllers.SecureStorageController;
|
||||
import org.whispersystems.textsecuregcm.controllers.SecureValueRecovery2Controller;
|
||||
import org.whispersystems.textsecuregcm.controllers.StickerController;
|
||||
import org.whispersystems.textsecuregcm.controllers.SubscriptionController;
|
||||
import org.whispersystems.textsecuregcm.currency.CoinMarketCapClient;
|
||||
import org.whispersystems.textsecuregcm.currency.CurrencyConversionManager;
|
||||
import org.whispersystems.textsecuregcm.currency.FixerClient;
|
||||
import org.whispersystems.textsecuregcm.experiment.ExperimentEnrollmentManager;
|
||||
import org.whispersystems.textsecuregcm.filters.RemoteDeprecationFilter;
|
||||
import org.whispersystems.textsecuregcm.filters.RequestStatisticsFilter;
|
||||
import org.whispersystems.textsecuregcm.filters.TimestampResponseFilter;
|
||||
import org.whispersystems.textsecuregcm.limits.DynamicRateLimiters;
|
||||
import org.whispersystems.textsecuregcm.limits.PushChallengeManager;
|
||||
import org.whispersystems.textsecuregcm.limits.RateLimitChallengeManager;
|
||||
import org.whispersystems.textsecuregcm.limits.RateLimiters;
|
||||
import org.whispersystems.textsecuregcm.mappers.CompletionExceptionMapper;
|
||||
import org.whispersystems.textsecuregcm.mappers.DeviceLimitExceededExceptionMapper;
|
||||
import org.whispersystems.textsecuregcm.mappers.IOExceptionMapper;
|
||||
import org.whispersystems.textsecuregcm.mappers.ImpossiblePhoneNumberExceptionMapper;
|
||||
import org.whispersystems.textsecuregcm.mappers.InvalidWebsocketAddressExceptionMapper;
|
||||
import org.whispersystems.textsecuregcm.mappers.JsonMappingExceptionMapper;
|
||||
import org.whispersystems.textsecuregcm.mappers.NonNormalizedPhoneNumberExceptionMapper;
|
||||
import org.whispersystems.textsecuregcm.mappers.RateLimitExceededExceptionMapper;
|
||||
import org.whispersystems.textsecuregcm.mappers.ServerRejectedExceptionMapper;
|
||||
import org.whispersystems.textsecuregcm.metrics.ApplicationShutdownMonitor;
|
||||
import org.whispersystems.textsecuregcm.metrics.BufferPoolGauges;
|
||||
import org.whispersystems.textsecuregcm.metrics.CpuUsageGauge;
|
||||
import org.whispersystems.textsecuregcm.metrics.FileDescriptorGauge;
|
||||
import org.whispersystems.textsecuregcm.metrics.FreeMemoryGauge;
|
||||
import org.whispersystems.textsecuregcm.metrics.GarbageCollectionGauges;
|
||||
import org.whispersystems.textsecuregcm.metrics.LettuceMetricsMeterFilter;
|
||||
import org.whispersystems.textsecuregcm.metrics.MaxFileDescriptorGauge;
|
||||
import org.whispersystems.textsecuregcm.metrics.MetricsApplicationEventListener;
|
||||
import org.whispersystems.textsecuregcm.metrics.MetricsRequestEventListener;
|
||||
import org.whispersystems.textsecuregcm.metrics.MicrometerRegistryManager;
|
||||
import org.whispersystems.textsecuregcm.metrics.NetworkReceivedGauge;
|
||||
import org.whispersystems.textsecuregcm.metrics.NetworkSentGauge;
|
||||
import org.whispersystems.textsecuregcm.metrics.OperatingSystemMemoryGauge;
|
||||
import org.whispersystems.textsecuregcm.metrics.ReportedMessageMetricsListener;
|
||||
import org.whispersystems.textsecuregcm.metrics.TrafficSource;
|
||||
import org.whispersystems.textsecuregcm.providers.MultiRecipientMessageProvider;
|
||||
import org.whispersystems.textsecuregcm.providers.RedisClientFactory;
|
||||
import org.whispersystems.textsecuregcm.providers.RedisClusterHealthCheck;
|
||||
import org.whispersystems.textsecuregcm.push.APNSender;
|
||||
import org.whispersystems.textsecuregcm.push.ApnPushNotificationScheduler;
|
||||
import org.whispersystems.textsecuregcm.push.ClientPresenceManager;
|
||||
import org.whispersystems.textsecuregcm.push.FcmSender;
|
||||
import org.whispersystems.textsecuregcm.push.MessageSender;
|
||||
import org.whispersystems.textsecuregcm.push.ProvisioningManager;
|
||||
import org.whispersystems.textsecuregcm.push.PushLatencyManager;
|
||||
import org.whispersystems.textsecuregcm.push.PushNotificationManager;
|
||||
import org.whispersystems.textsecuregcm.push.ReceiptSender;
|
||||
import org.whispersystems.textsecuregcm.redis.ConnectionEventLogger;
|
||||
import org.whispersystems.textsecuregcm.redis.FaultTolerantRedisCluster;
|
||||
import org.whispersystems.textsecuregcm.redis.ReplicatedJedisPool;
|
||||
import org.whispersystems.textsecuregcm.registration.RegistrationServiceClient;
|
||||
import org.whispersystems.textsecuregcm.s3.PolicySigner;
|
||||
import org.whispersystems.textsecuregcm.s3.PostPolicyGenerator;
|
||||
import org.whispersystems.textsecuregcm.securebackup.SecureBackupClient;
|
||||
import org.whispersystems.textsecuregcm.securestorage.SecureStorageClient;
|
||||
import org.whispersystems.textsecuregcm.spam.FilterSpam;
|
||||
import org.whispersystems.textsecuregcm.spam.RateLimitChallengeListener;
|
||||
import org.whispersystems.textsecuregcm.spam.ReportSpamTokenProvider;
|
||||
import org.whispersystems.textsecuregcm.spam.SpamFilter;
|
||||
import org.whispersystems.textsecuregcm.sqs.DirectoryQueue;
|
||||
import org.whispersystems.textsecuregcm.storage.AccountCleaner;
|
||||
import org.whispersystems.textsecuregcm.storage.AccountDatabaseCrawler;
|
||||
import org.whispersystems.textsecuregcm.storage.AccountDatabaseCrawlerCache;
|
||||
import org.whispersystems.textsecuregcm.storage.AccountDatabaseCrawlerListener;
|
||||
import org.whispersystems.textsecuregcm.storage.Accounts;
|
||||
import org.whispersystems.textsecuregcm.storage.AccountsManager;
|
||||
import org.whispersystems.textsecuregcm.storage.ChangeNumberManager;
|
||||
import org.whispersystems.textsecuregcm.storage.ContactDiscoveryWriter;
|
||||
import org.whispersystems.textsecuregcm.storage.DeletedAccounts;
|
||||
import org.whispersystems.textsecuregcm.storage.DeletedAccountsDirectoryReconciler;
|
||||
import org.whispersystems.textsecuregcm.storage.DeletedAccountsManager;
|
||||
import org.whispersystems.textsecuregcm.storage.DeletedAccountsTableCrawler;
|
||||
import org.whispersystems.textsecuregcm.storage.DirectoryReconciler;
|
||||
import org.whispersystems.textsecuregcm.storage.DirectoryReconciliationClient;
|
||||
import org.whispersystems.textsecuregcm.storage.DynamicConfigurationManager;
|
||||
import org.whispersystems.textsecuregcm.storage.IssuedReceiptsManager;
|
||||
import org.whispersystems.textsecuregcm.storage.Keys;
|
||||
import org.whispersystems.textsecuregcm.storage.MessagePersister;
|
||||
import org.whispersystems.textsecuregcm.storage.MessagesCache;
|
||||
import org.whispersystems.textsecuregcm.storage.MessagesDynamoDb;
|
||||
import org.whispersystems.textsecuregcm.storage.MessagesManager;
|
||||
import org.whispersystems.textsecuregcm.storage.NonNormalizedAccountCrawlerListener;
|
||||
import org.whispersystems.textsecuregcm.storage.PhoneNumberIdentifiers;
|
||||
import org.whispersystems.textsecuregcm.storage.Profiles;
|
||||
import org.whispersystems.textsecuregcm.storage.ProfilesManager;
|
||||
import org.whispersystems.textsecuregcm.storage.PubSubManager;
|
||||
import org.whispersystems.textsecuregcm.storage.PushChallengeDynamoDb;
|
||||
import org.whispersystems.textsecuregcm.storage.PushFeedbackProcessor;
|
||||
import org.whispersystems.textsecuregcm.storage.RedeemedReceiptsManager;
|
||||
import org.whispersystems.textsecuregcm.storage.RegistrationRecoveryPasswords;
|
||||
import org.whispersystems.textsecuregcm.storage.RegistrationRecoveryPasswordsManager;
|
||||
import org.whispersystems.textsecuregcm.storage.RemoteConfigs;
|
||||
import org.whispersystems.textsecuregcm.storage.RemoteConfigsManager;
|
||||
import org.whispersystems.textsecuregcm.storage.ReportMessageDynamoDb;
|
||||
import org.whispersystems.textsecuregcm.storage.ReportMessageManager;
|
||||
import org.whispersystems.textsecuregcm.storage.StoredVerificationCodeManager;
|
||||
import org.whispersystems.textsecuregcm.storage.SubscriptionManager;
|
||||
import org.whispersystems.textsecuregcm.storage.VerificationCodeStore;
|
||||
import org.whispersystems.textsecuregcm.subscriptions.BraintreeManager;
|
||||
import org.whispersystems.textsecuregcm.subscriptions.StripeManager;
|
||||
import org.whispersystems.textsecuregcm.util.Constants;
|
||||
import org.whispersystems.textsecuregcm.util.DynamoDbFromConfig;
|
||||
import org.whispersystems.textsecuregcm.util.HostnameUtil;
|
||||
import org.whispersystems.textsecuregcm.util.UsernameHashZkProofVerifier;
|
||||
import org.whispersystems.textsecuregcm.util.logging.LoggingUnhandledExceptionMapper;
|
||||
import org.whispersystems.textsecuregcm.util.logging.UncaughtExceptionHandler;
|
||||
import org.whispersystems.textsecuregcm.websocket.AuthenticatedConnectListener;
|
||||
import org.whispersystems.textsecuregcm.websocket.ProvisioningConnectListener;
|
||||
import org.whispersystems.textsecuregcm.websocket.WebSocketAccountAuthenticator;
|
||||
import org.whispersystems.textsecuregcm.workers.AssignUsernameCommand;
|
||||
import org.whispersystems.textsecuregcm.workers.CertificateCommand;
|
||||
import org.whispersystems.textsecuregcm.workers.CheckDynamicConfigurationCommand;
|
||||
import org.whispersystems.textsecuregcm.workers.DeleteUserCommand;
|
||||
import org.whispersystems.textsecuregcm.workers.ReserveUsernameCommand;
|
||||
import org.whispersystems.textsecuregcm.workers.ServerVersionCommand;
|
||||
import org.whispersystems.textsecuregcm.workers.SetCrawlerAccelerationTask;
|
||||
import org.whispersystems.textsecuregcm.workers.SetRequestLoggingEnabledTask;
|
||||
import org.whispersystems.textsecuregcm.workers.SetUserDiscoverabilityCommand;
|
||||
import org.whispersystems.textsecuregcm.workers.ZkParamsCommand;
|
||||
import org.whispersystems.websocket.WebSocketResourceProviderFactory;
|
||||
import org.whispersystems.websocket.setup.WebSocketEnvironment;
|
||||
import reactor.core.scheduler.Schedulers;
|
||||
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
|
||||
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
|
||||
import software.amazon.awssdk.regions.Region;
|
||||
import software.amazon.awssdk.services.dynamodb.DynamoDbAsyncClient;
|
||||
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
|
||||
import software.amazon.awssdk.services.s3.S3Client;
|
||||
|
||||
public class WhisperServerService extends Application<WhisperServerConfiguration> {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(WhisperServerService.class);
|
||||
|
||||
@Override
|
||||
public void initialize(Bootstrap<WhisperServerConfiguration> bootstrap) {
|
||||
bootstrap.addCommand(new DeleteUserCommand());
|
||||
bootstrap.addCommand(new CertificateCommand());
|
||||
bootstrap.addCommand(new ZkParamsCommand());
|
||||
bootstrap.addCommand(new ServerVersionCommand());
|
||||
bootstrap.addCommand(new CheckDynamicConfigurationCommand());
|
||||
bootstrap.addCommand(new SetUserDiscoverabilityCommand());
|
||||
bootstrap.addCommand(new ReserveUsernameCommand());
|
||||
bootstrap.addCommand(new AssignUsernameCommand());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "whisper-server";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(WhisperServerConfiguration config, Environment environment) throws Exception {
|
||||
final Clock clock = Clock.systemUTC();
|
||||
final int availableProcessors = Runtime.getRuntime().availableProcessors();
|
||||
|
||||
UncaughtExceptionHandler.register();
|
||||
|
||||
SharedMetricRegistries.add(Constants.METRICS_NAME, environment.metrics());
|
||||
|
||||
final DistributionStatisticConfig defaultDistributionStatisticConfig = DistributionStatisticConfig.builder()
|
||||
.percentiles(.75, .95, .99, .999)
|
||||
.build();
|
||||
|
||||
{
|
||||
final DatadogMeterRegistry datadogMeterRegistry = new DatadogMeterRegistry(
|
||||
config.getDatadogConfiguration(), io.micrometer.core.instrument.Clock.SYSTEM);
|
||||
|
||||
datadogMeterRegistry.config().commonTags(
|
||||
Tags.of(
|
||||
"service", "chat",
|
||||
"host", HostnameUtil.getLocalHostname(),
|
||||
"version", WhisperServerVersion.getServerVersion(),
|
||||
"env", config.getDatadogConfiguration().getEnvironment()))
|
||||
.meterFilter(MeterFilter.denyNameStartsWith(MetricsRequestEventListener.REQUEST_COUNTER_NAME))
|
||||
.meterFilter(MeterFilter.denyNameStartsWith(MetricsRequestEventListener.ANDROID_REQUEST_COUNTER_NAME))
|
||||
.meterFilter(MeterFilter.denyNameStartsWith(MetricsRequestEventListener.DESKTOP_REQUEST_COUNTER_NAME))
|
||||
.meterFilter(MeterFilter.denyNameStartsWith(MetricsRequestEventListener.IOS_REQUEST_COUNTER_NAME))
|
||||
.meterFilter(new LettuceMetricsMeterFilter())
|
||||
.meterFilter(new MeterFilter() {
|
||||
@Override
|
||||
public DistributionStatisticConfig configure(final Id id, final DistributionStatisticConfig config) {
|
||||
return defaultDistributionStatisticConfig.merge(config);
|
||||
}
|
||||
});
|
||||
|
||||
Metrics.addRegistry(datadogMeterRegistry);
|
||||
}
|
||||
|
||||
environment.lifecycle().manage(new MicrometerRegistryManager(Metrics.globalRegistry));
|
||||
|
||||
environment.getObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||
environment.getObjectMapper().setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.NONE);
|
||||
environment.getObjectMapper().setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
|
||||
|
||||
HeaderControlledResourceBundleLookup headerControlledResourceBundleLookup =
|
||||
new HeaderControlledResourceBundleLookup();
|
||||
ConfiguredProfileBadgeConverter profileBadgeConverter = new ConfiguredProfileBadgeConverter(
|
||||
clock, config.getBadges(), headerControlledResourceBundleLookup);
|
||||
ResourceBundleLevelTranslator resourceBundleLevelTranslator = new ResourceBundleLevelTranslator(
|
||||
headerControlledResourceBundleLookup);
|
||||
|
||||
DynamoDbAsyncClient dynamoDbAsyncClient = DynamoDbFromConfig.asyncClient(
|
||||
config.getDynamoDbClientConfiguration(),
|
||||
software.amazon.awssdk.auth.credentials.InstanceProfileCredentialsProvider.create());
|
||||
|
||||
DynamoDbClient dynamoDbClient = DynamoDbFromConfig.client(
|
||||
config.getDynamoDbClientConfiguration(),
|
||||
software.amazon.awssdk.auth.credentials.InstanceProfileCredentialsProvider.create());
|
||||
|
||||
AmazonDynamoDB deletedAccountsLockDynamoDbClient = AmazonDynamoDBClientBuilder.standard()
|
||||
.withRegion(config.getDynamoDbClientConfiguration().getRegion())
|
||||
.withClientConfiguration(new ClientConfiguration().withClientExecutionTimeout(
|
||||
((int) config.getDynamoDbClientConfiguration().getClientExecutionTimeout().toMillis()))
|
||||
.withRequestTimeout(
|
||||
(int) config.getDynamoDbClientConfiguration().getClientRequestTimeout().toMillis()))
|
||||
.withCredentials(InstanceProfileCredentialsProvider.getInstance())
|
||||
.build();
|
||||
|
||||
DeletedAccounts deletedAccounts = new DeletedAccounts(dynamoDbClient,
|
||||
config.getDynamoDbTables().getDeletedAccounts().getTableName(),
|
||||
config.getDynamoDbTables().getDeletedAccounts().getNeedsReconciliationIndexName());
|
||||
|
||||
DynamicConfigurationManager<DynamicConfiguration> dynamicConfigurationManager =
|
||||
new DynamicConfigurationManager<>(config.getAppConfig().getApplication(),
|
||||
config.getAppConfig().getEnvironment(),
|
||||
config.getAppConfig().getConfigurationName(),
|
||||
DynamicConfiguration.class);
|
||||
|
||||
BlockingQueue<Runnable> messageDeletionQueue = new LinkedBlockingQueue<>();
|
||||
Metrics.gaugeCollectionSize(name(getClass(), "messageDeletionQueueSize"), Collections.emptyList(),
|
||||
messageDeletionQueue);
|
||||
ExecutorService messageDeletionAsyncExecutor = environment.lifecycle()
|
||||
.executorService(name(getClass(), "messageDeletionAsyncExecutor-%d")).maxThreads(16)
|
||||
.workQueue(messageDeletionQueue).build();
|
||||
|
||||
Accounts accounts = new Accounts(
|
||||
dynamoDbClient,
|
||||
dynamoDbAsyncClient,
|
||||
config.getDynamoDbTables().getAccounts().getTableName(),
|
||||
config.getDynamoDbTables().getAccounts().getPhoneNumberTableName(),
|
||||
config.getDynamoDbTables().getAccounts().getPhoneNumberIdentifierTableName(),
|
||||
config.getDynamoDbTables().getAccounts().getUsernamesTableName(),
|
||||
config.getDynamoDbTables().getAccounts().getScanPageSize());
|
||||
PhoneNumberIdentifiers phoneNumberIdentifiers = new PhoneNumberIdentifiers(dynamoDbClient,
|
||||
config.getDynamoDbTables().getPhoneNumberIdentifiers().getTableName());
|
||||
Profiles profiles = new Profiles(dynamoDbClient, dynamoDbAsyncClient,
|
||||
config.getDynamoDbTables().getProfiles().getTableName());
|
||||
Keys keys = new Keys(dynamoDbClient, config.getDynamoDbTables().getKeys().getTableName());
|
||||
MessagesDynamoDb messagesDynamoDb = new MessagesDynamoDb(dynamoDbClient, dynamoDbAsyncClient,
|
||||
config.getDynamoDbTables().getMessages().getTableName(),
|
||||
config.getDynamoDbTables().getMessages().getExpiration(),
|
||||
messageDeletionAsyncExecutor);
|
||||
RemoteConfigs remoteConfigs = new RemoteConfigs(dynamoDbClient,
|
||||
config.getDynamoDbTables().getRemoteConfig().getTableName());
|
||||
PushChallengeDynamoDb pushChallengeDynamoDb = new PushChallengeDynamoDb(dynamoDbClient,
|
||||
config.getDynamoDbTables().getPushChallenge().getTableName());
|
||||
ReportMessageDynamoDb reportMessageDynamoDb = new ReportMessageDynamoDb(dynamoDbClient,
|
||||
config.getDynamoDbTables().getReportMessage().getTableName(),
|
||||
config.getReportMessageConfiguration().getReportTtl());
|
||||
VerificationCodeStore pendingAccounts = new VerificationCodeStore(dynamoDbClient,
|
||||
config.getDynamoDbTables().getPendingAccounts().getTableName());
|
||||
VerificationCodeStore pendingDevices = new VerificationCodeStore(dynamoDbClient,
|
||||
config.getDynamoDbTables().getPendingDevices().getTableName());
|
||||
RegistrationRecoveryPasswords registrationRecoveryPasswords = new RegistrationRecoveryPasswords(
|
||||
config.getDynamoDbTables().getRegistrationRecovery().getTableName(),
|
||||
config.getDynamoDbTables().getRegistrationRecovery().getExpiration(),
|
||||
dynamoDbClient,
|
||||
dynamoDbAsyncClient
|
||||
);
|
||||
|
||||
reactor.util.Metrics.MicrometerConfiguration.useRegistry(Metrics.globalRegistry);
|
||||
Schedulers.enableMetrics();
|
||||
|
||||
RedisClientFactory pubSubClientFactory = new RedisClientFactory("pubsub_cache",
|
||||
config.getPubsubCacheConfiguration().getUrl(), config.getPubsubCacheConfiguration().getReplicaUrls(),
|
||||
config.getPubsubCacheConfiguration().getCircuitBreakerConfiguration());
|
||||
ReplicatedJedisPool pubsubClient = pubSubClientFactory.getRedisClientPool();
|
||||
|
||||
MicrometerOptions options = MicrometerOptions.builder().build();
|
||||
ClientResources redisClientResources = ClientResources.builder()
|
||||
.commandLatencyRecorder(new MicrometerCommandLatencyRecorder(Metrics.globalRegistry, options)).build();
|
||||
ConnectionEventLogger.logConnectionEvents(redisClientResources);
|
||||
|
||||
FaultTolerantRedisCluster cacheCluster = new FaultTolerantRedisCluster("main_cache_cluster", config.getCacheClusterConfiguration(), redisClientResources);
|
||||
FaultTolerantRedisCluster messagesCluster = new FaultTolerantRedisCluster("messages_cluster", config.getMessageCacheConfiguration().getRedisClusterConfiguration(), redisClientResources);
|
||||
FaultTolerantRedisCluster clientPresenceCluster = new FaultTolerantRedisCluster("client_presence_cluster", config.getClientPresenceClusterConfiguration(), redisClientResources);
|
||||
FaultTolerantRedisCluster metricsCluster = new FaultTolerantRedisCluster("metrics_cluster", config.getMetricsClusterConfiguration(), redisClientResources);
|
||||
FaultTolerantRedisCluster pushSchedulerCluster = new FaultTolerantRedisCluster("push_scheduler", config.getPushSchedulerCluster(), redisClientResources);
|
||||
FaultTolerantRedisCluster rateLimitersCluster = new FaultTolerantRedisCluster("rate_limiters", config.getRateLimitersCluster(), redisClientResources);
|
||||
|
||||
final BlockingQueue<Runnable> keyspaceNotificationDispatchQueue = new ArrayBlockingQueue<>(100_000);
|
||||
Metrics.gaugeCollectionSize(name(getClass(), "keyspaceNotificationDispatchQueueSize"), Collections.emptyList(), keyspaceNotificationDispatchQueue);
|
||||
final BlockingQueue<Runnable> receiptSenderQueue = new LinkedBlockingQueue<>();
|
||||
Metrics.gaugeCollectionSize(name(getClass(), "receiptSenderQueue"), Collections.emptyList(), receiptSenderQueue);
|
||||
|
||||
final BlockingQueue<Runnable> fcmSenderQueue = new LinkedBlockingQueue<>();
|
||||
Metrics.gaugeCollectionSize(name(getClass(), "fcmSenderQueue"), Collections.emptyList(), fcmSenderQueue);
|
||||
|
||||
ScheduledExecutorService recurringJobExecutor = environment.lifecycle()
|
||||
.scheduledExecutorService(name(getClass(), "recurringJob-%d")).threads(6).build();
|
||||
ScheduledExecutorService websocketScheduledExecutor = environment.lifecycle().scheduledExecutorService(name(getClass(), "websocket-%d")).threads(8).build();
|
||||
ExecutorService keyspaceNotificationDispatchExecutor = environment.lifecycle().executorService(name(getClass(), "keyspaceNotification-%d")).maxThreads(16).workQueue(keyspaceNotificationDispatchQueue).build();
|
||||
ExecutorService apnSenderExecutor = environment.lifecycle().executorService(name(getClass(), "apnSender-%d")).maxThreads(1).minThreads(1).build();
|
||||
ExecutorService fcmSenderExecutor = environment.lifecycle().executorService(name(getClass(), "fcmSender-%d")).maxThreads(32).minThreads(32).workQueue(fcmSenderQueue).build();
|
||||
ExecutorService backupServiceExecutor = environment.lifecycle().executorService(name(getClass(), "backupService-%d")).maxThreads(1).minThreads(1).build();
|
||||
ExecutorService storageServiceExecutor = environment.lifecycle().executorService(name(getClass(), "storageService-%d")).maxThreads(1).minThreads(1).build();
|
||||
ExecutorService accountDeletionExecutor = environment.lifecycle().executorService(name(getClass(), "accountCleaner-%d")).maxThreads(16).minThreads(16).build();
|
||||
|
||||
// TODO: generally speaking this is a DynamoDB I/O executor for the accounts table; we should eventually have a general executor for speaking to the accounts table, but most of the server is still synchronous so this isn't widely useful yet
|
||||
ExecutorService batchIdentityCheckExecutor = environment.lifecycle().executorService(name(getClass(), "batchIdentityCheck-%d")).minThreads(32).maxThreads(32).build();
|
||||
ExecutorService multiRecipientMessageExecutor = environment.lifecycle()
|
||||
.executorService(name(getClass(), "multiRecipientMessage-%d")).minThreads(64).maxThreads(64).build();
|
||||
ExecutorService subscriptionProcessorExecutor = environment.lifecycle()
|
||||
.executorService(name(getClass(), "subscriptionProcessor-%d"))
|
||||
.maxThreads(availableProcessors) // mostly this is IO bound so tying to number of processors is tenuous at best
|
||||
.minThreads(availableProcessors) // mostly this is IO bound so tying to number of processors is tenuous at best
|
||||
.allowCoreThreadTimeOut(true).
|
||||
build();
|
||||
ExecutorService receiptSenderExecutor = environment.lifecycle()
|
||||
.executorService(name(getClass(), "receiptSender-%d"))
|
||||
.maxThreads(2)
|
||||
.minThreads(2)
|
||||
.workQueue(receiptSenderQueue)
|
||||
.rejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy())
|
||||
.build();
|
||||
ExecutorService registrationCallbackExecutor = environment.lifecycle()
|
||||
.executorService(name(getClass(), "registration-%d"))
|
||||
.maxThreads(2)
|
||||
.minThreads(2)
|
||||
.build();
|
||||
|
||||
final AdminEventLogger adminEventLogger = new GoogleCloudAdminEventLogger(
|
||||
LoggingOptions.newBuilder().setProjectId(config.getAdminEventLoggingConfiguration().projectId())
|
||||
.setCredentials(GoogleCredentials.fromStream(new ByteArrayInputStream(
|
||||
config.getAdminEventLoggingConfiguration().credentials().getBytes(StandardCharsets.UTF_8))))
|
||||
.build().getService(),
|
||||
config.getAdminEventLoggingConfiguration().projectId(),
|
||||
config.getAdminEventLoggingConfiguration().logName());
|
||||
|
||||
StripeManager stripeManager = new StripeManager(config.getStripe().apiKey(), subscriptionProcessorExecutor,
|
||||
config.getStripe().idempotencyKeyGenerator(), config.getStripe().boostDescription(), config.getStripe()
|
||||
.supportedCurrencies());
|
||||
BraintreeManager braintreeManager = new BraintreeManager(config.getBraintree().merchantId(),
|
||||
config.getBraintree().publicKey(), config.getBraintree().privateKey(), config.getBraintree().environment(),
|
||||
config.getBraintree().supportedCurrencies(), config.getBraintree().merchantAccounts(),
|
||||
config.getBraintree().graphqlUrl(), config.getBraintree().circuitBreaker(), subscriptionProcessorExecutor);
|
||||
|
||||
ExternalServiceCredentialsGenerator directoryCredentialsGenerator = DirectoryController.credentialsGenerator(
|
||||
config.getDirectoryConfiguration().getDirectoryClientConfiguration());
|
||||
ExternalServiceCredentialsGenerator directoryV2CredentialsGenerator = DirectoryV2Controller.credentialsGenerator(
|
||||
config.getDirectoryV2Configuration().getDirectoryV2ClientConfiguration());
|
||||
ExternalServiceCredentialsGenerator storageCredentialsGenerator = SecureStorageController.credentialsGenerator(
|
||||
config.getSecureStorageServiceConfiguration());
|
||||
ExternalServiceCredentialsGenerator backupCredentialsGenerator = SecureBackupController.credentialsGenerator(
|
||||
config.getSecureBackupServiceConfiguration());
|
||||
ExternalServiceCredentialsGenerator paymentsCredentialsGenerator = PaymentsController.credentialsGenerator(
|
||||
config.getPaymentsServiceConfiguration());
|
||||
ExternalServiceCredentialsGenerator artCredentialsGenerator = ArtController.credentialsGenerator(
|
||||
config.getArtServiceConfiguration());
|
||||
ExternalServiceCredentialsGenerator svr2CredentialsGenerator = SecureValueRecovery2Controller.credentialsGenerator(
|
||||
config.getSvr2Configuration());
|
||||
|
||||
dynamicConfigurationManager.start();
|
||||
|
||||
ExperimentEnrollmentManager experimentEnrollmentManager = new ExperimentEnrollmentManager(dynamicConfigurationManager);
|
||||
RegistrationRecoveryPasswordsManager registrationRecoveryPasswordsManager = new RegistrationRecoveryPasswordsManager(registrationRecoveryPasswords);
|
||||
UsernameHashZkProofVerifier usernameHashZkProofVerifier = new UsernameHashZkProofVerifier();
|
||||
|
||||
RegistrationServiceClient registrationServiceClient = new RegistrationServiceClient(config.getRegistrationServiceConfiguration().getHost(), config.getRegistrationServiceConfiguration().getPort(), config.getRegistrationServiceConfiguration().getApiKey(), config.getRegistrationServiceConfiguration().getRegistrationCaCertificate(), registrationCallbackExecutor);
|
||||
SecureBackupClient secureBackupClient = new SecureBackupClient(backupCredentialsGenerator, backupServiceExecutor, config.getSecureBackupServiceConfiguration());
|
||||
SecureStorageClient secureStorageClient = new SecureStorageClient(storageCredentialsGenerator, storageServiceExecutor, config.getSecureStorageServiceConfiguration());
|
||||
ClientPresenceManager clientPresenceManager = new ClientPresenceManager(clientPresenceCluster, recurringJobExecutor, keyspaceNotificationDispatchExecutor);
|
||||
DirectoryQueue directoryQueue = new DirectoryQueue(config.getDirectoryConfiguration().getSqsConfiguration());
|
||||
StoredVerificationCodeManager pendingAccountsManager = new StoredVerificationCodeManager(pendingAccounts);
|
||||
StoredVerificationCodeManager pendingDevicesManager = new StoredVerificationCodeManager(pendingDevices);
|
||||
ProfilesManager profilesManager = new ProfilesManager(profiles, cacheCluster);
|
||||
MessagesCache messagesCache = new MessagesCache(messagesCluster, messagesCluster, Clock.systemUTC(),
|
||||
keyspaceNotificationDispatchExecutor, messageDeletionAsyncExecutor);
|
||||
PushLatencyManager pushLatencyManager = new PushLatencyManager(metricsCluster, dynamicConfigurationManager);
|
||||
ReportMessageManager reportMessageManager = new ReportMessageManager(reportMessageDynamoDb, rateLimitersCluster,
|
||||
config.getReportMessageConfiguration().getCounterTtl());
|
||||
MessagesManager messagesManager = new MessagesManager(messagesDynamoDb, messagesCache, reportMessageManager,
|
||||
messageDeletionAsyncExecutor);
|
||||
DeletedAccountsManager deletedAccountsManager = new DeletedAccountsManager(deletedAccounts,
|
||||
deletedAccountsLockDynamoDbClient, config.getDynamoDbTables().getDeletedAccountsLock().getTableName());
|
||||
AccountsManager accountsManager = new AccountsManager(accounts, phoneNumberIdentifiers, cacheCluster,
|
||||
deletedAccountsManager, directoryQueue, keys, messagesManager, profilesManager,
|
||||
pendingAccountsManager, secureStorageClient, secureBackupClient, clientPresenceManager,
|
||||
experimentEnrollmentManager, registrationRecoveryPasswordsManager, clock);
|
||||
RemoteConfigsManager remoteConfigsManager = new RemoteConfigsManager(remoteConfigs);
|
||||
DispatchManager dispatchManager = new DispatchManager(pubSubClientFactory, Optional.empty());
|
||||
PubSubManager pubSubManager = new PubSubManager(pubsubClient, dispatchManager);
|
||||
APNSender apnSender = new APNSender(apnSenderExecutor, config.getApnConfiguration());
|
||||
FcmSender fcmSender = new FcmSender(fcmSenderExecutor, config.getFcmConfiguration().credentials());
|
||||
ApnPushNotificationScheduler apnPushNotificationScheduler = new ApnPushNotificationScheduler(pushSchedulerCluster, apnSender, accountsManager);
|
||||
PushNotificationManager pushNotificationManager = new PushNotificationManager(accountsManager, apnSender, fcmSender, apnPushNotificationScheduler, pushLatencyManager, dynamicConfigurationManager);
|
||||
RateLimiters rateLimiters = new RateLimiters(config.getLimitsConfiguration(), rateLimitersCluster);
|
||||
DynamicRateLimiters dynamicRateLimiters = new DynamicRateLimiters(rateLimitersCluster, dynamicConfigurationManager);
|
||||
ProvisioningManager provisioningManager = new ProvisioningManager(pubSubManager);
|
||||
IssuedReceiptsManager issuedReceiptsManager = new IssuedReceiptsManager(
|
||||
config.getDynamoDbTables().getIssuedReceipts().getTableName(),
|
||||
config.getDynamoDbTables().getIssuedReceipts().getExpiration(),
|
||||
dynamoDbAsyncClient,
|
||||
config.getDynamoDbTables().getIssuedReceipts().getGenerator());
|
||||
RedeemedReceiptsManager redeemedReceiptsManager = new RedeemedReceiptsManager(
|
||||
clock,
|
||||
config.getDynamoDbTables().getRedeemedReceipts().getTableName(),
|
||||
dynamoDbAsyncClient,
|
||||
config.getDynamoDbTables().getRedeemedReceipts().getExpiration());
|
||||
SubscriptionManager subscriptionManager = new SubscriptionManager(
|
||||
config.getDynamoDbTables().getSubscriptions().getTableName(), dynamoDbAsyncClient);
|
||||
|
||||
final RegistrationLockVerificationManager registrationLockVerificationManager = new RegistrationLockVerificationManager(
|
||||
accountsManager, clientPresenceManager, backupCredentialsGenerator, rateLimiters);
|
||||
final PhoneVerificationTokenManager phoneVerificationTokenManager = new PhoneVerificationTokenManager(
|
||||
registrationServiceClient, registrationRecoveryPasswordsManager);
|
||||
|
||||
final ReportedMessageMetricsListener reportedMessageMetricsListener = new ReportedMessageMetricsListener(
|
||||
accountsManager);
|
||||
reportMessageManager.addListener(reportedMessageMetricsListener);
|
||||
|
||||
final AccountAuthenticator accountAuthenticator = new AccountAuthenticator(accountsManager);
|
||||
final DisabledPermittedAccountAuthenticator disabledPermittedAccountAuthenticator = new DisabledPermittedAccountAuthenticator(
|
||||
accountsManager);
|
||||
|
||||
final MessageSender messageSender = new MessageSender(clientPresenceManager, messagesManager,
|
||||
pushNotificationManager,
|
||||
pushLatencyManager);
|
||||
final ReceiptSender receiptSender = new ReceiptSender(accountsManager, messageSender, receiptSenderExecutor);
|
||||
final TurnTokenGenerator turnTokenGenerator = new TurnTokenGenerator(dynamicConfigurationManager);
|
||||
|
||||
RecaptchaClient recaptchaClient = new RecaptchaClient(
|
||||
config.getRecaptchaConfiguration().getProjectPath(),
|
||||
config.getRecaptchaConfiguration().getCredentialConfigurationJson(),
|
||||
dynamicConfigurationManager);
|
||||
HttpClient hcaptchaHttpClient = HttpClient.newBuilder().version(HttpClient.Version.HTTP_2).connectTimeout(Duration.ofSeconds(10)).build();
|
||||
HCaptchaClient hCaptchaClient = new HCaptchaClient(config.getHCaptchaConfiguration().apiKey(), hcaptchaHttpClient, dynamicConfigurationManager);
|
||||
CaptchaChecker captchaChecker = new CaptchaChecker(List.of(recaptchaClient, hCaptchaClient));
|
||||
|
||||
PushChallengeManager pushChallengeManager = new PushChallengeManager(pushNotificationManager, pushChallengeDynamoDb);
|
||||
RateLimitChallengeManager rateLimitChallengeManager = new RateLimitChallengeManager(pushChallengeManager,
|
||||
captchaChecker, dynamicRateLimiters);
|
||||
|
||||
MessagePersister messagePersister = new MessagePersister(messagesCache, messagesManager, accountsManager, dynamicConfigurationManager, Duration.ofMinutes(config.getMessageCacheConfiguration().getPersistDelayMinutes()));
|
||||
ChangeNumberManager changeNumberManager = new ChangeNumberManager(messageSender, accountsManager);
|
||||
|
||||
final List<AccountDatabaseCrawlerListener> directoryReconciliationAccountDatabaseCrawlerListeners = new ArrayList<>();
|
||||
final List<DeletedAccountsDirectoryReconciler> deletedAccountsDirectoryReconcilers = new ArrayList<>();
|
||||
for (DirectoryServerConfiguration directoryServerConfiguration : config.getDirectoryConfiguration()
|
||||
.getDirectoryServerConfiguration()) {
|
||||
final DirectoryReconciliationClient directoryReconciliationClient = new DirectoryReconciliationClient(
|
||||
directoryServerConfiguration);
|
||||
final DirectoryReconciler directoryReconciler = new DirectoryReconciler(
|
||||
directoryServerConfiguration.getReplicationName(), directoryReconciliationClient,
|
||||
dynamicConfigurationManager);
|
||||
// reconcilers are read-only
|
||||
directoryReconciliationAccountDatabaseCrawlerListeners.add(directoryReconciler);
|
||||
|
||||
final DeletedAccountsDirectoryReconciler deletedAccountsDirectoryReconciler = new DeletedAccountsDirectoryReconciler(
|
||||
directoryServerConfiguration.getReplicationName(), directoryReconciliationClient);
|
||||
deletedAccountsDirectoryReconcilers.add(deletedAccountsDirectoryReconciler);
|
||||
}
|
||||
|
||||
AccountDatabaseCrawlerCache directoryReconciliationAccountDatabaseCrawlerCache = new AccountDatabaseCrawlerCache(
|
||||
cacheCluster, AccountDatabaseCrawlerCache.DIRECTORY_RECONCILER_PREFIX);
|
||||
AccountDatabaseCrawler directoryReconciliationAccountDatabaseCrawler = new AccountDatabaseCrawler(
|
||||
"Reconciliation crawler",
|
||||
accountsManager,
|
||||
directoryReconciliationAccountDatabaseCrawlerCache, directoryReconciliationAccountDatabaseCrawlerListeners,
|
||||
config.getAccountDatabaseCrawlerConfiguration().getChunkSize(),
|
||||
config.getAccountDatabaseCrawlerConfiguration().getChunkIntervalMs()
|
||||
);
|
||||
|
||||
AccountDatabaseCrawlerCache accountCleanerAccountDatabaseCrawlerCache =
|
||||
new AccountDatabaseCrawlerCache(cacheCluster, AccountDatabaseCrawlerCache.ACCOUNT_CLEANER_PREFIX);
|
||||
AccountDatabaseCrawler accountCleanerAccountDatabaseCrawler = new AccountDatabaseCrawler("Account cleaner crawler",
|
||||
accountsManager,
|
||||
accountCleanerAccountDatabaseCrawlerCache, List.of(new AccountCleaner(accountsManager, accountDeletionExecutor)),
|
||||
config.getAccountDatabaseCrawlerConfiguration().getChunkSize(),
|
||||
config.getAccountDatabaseCrawlerConfiguration().getChunkIntervalMs()
|
||||
);
|
||||
|
||||
// TODO listeners must be ordered so that ones that directly update accounts come last, so that read-only ones are not working with stale data
|
||||
final List<AccountDatabaseCrawlerListener> accountDatabaseCrawlerListeners = List.of(
|
||||
new NonNormalizedAccountCrawlerListener(accountsManager, metricsCluster),
|
||||
new ContactDiscoveryWriter(accountsManager),
|
||||
// PushFeedbackProcessor may update device properties
|
||||
new PushFeedbackProcessor(accountsManager));
|
||||
|
||||
AccountDatabaseCrawlerCache accountDatabaseCrawlerCache = new AccountDatabaseCrawlerCache(cacheCluster,
|
||||
AccountDatabaseCrawlerCache.GENERAL_PURPOSE_PREFIX);
|
||||
AccountDatabaseCrawler accountDatabaseCrawler = new AccountDatabaseCrawler("General-purpose account crawler",
|
||||
accountsManager,
|
||||
accountDatabaseCrawlerCache, accountDatabaseCrawlerListeners,
|
||||
config.getAccountDatabaseCrawlerConfiguration().getChunkSize(),
|
||||
config.getAccountDatabaseCrawlerConfiguration().getChunkIntervalMs()
|
||||
);
|
||||
|
||||
DeletedAccountsTableCrawler deletedAccountsTableCrawler = new DeletedAccountsTableCrawler(deletedAccountsManager, deletedAccountsDirectoryReconcilers, cacheCluster, recurringJobExecutor);
|
||||
|
||||
HttpClient currencyClient = HttpClient.newBuilder().version(HttpClient.Version.HTTP_2).connectTimeout(Duration.ofSeconds(10)).build();
|
||||
FixerClient fixerClient = new FixerClient(currencyClient, config.getPaymentsServiceConfiguration().getFixerApiKey());
|
||||
CoinMarketCapClient coinMarketCapClient = new CoinMarketCapClient(currencyClient, config.getPaymentsServiceConfiguration().getCoinMarketCapApiKey(), config.getPaymentsServiceConfiguration().getCoinMarketCapCurrencyIds());
|
||||
CurrencyConversionManager currencyManager = new CurrencyConversionManager(fixerClient, coinMarketCapClient,
|
||||
cacheCluster, config.getPaymentsServiceConfiguration().getPaymentCurrencies(), Clock.systemUTC());
|
||||
|
||||
environment.lifecycle().manage(apnSender);
|
||||
environment.lifecycle().manage(apnPushNotificationScheduler);
|
||||
environment.lifecycle().manage(pubSubManager);
|
||||
environment.lifecycle().manage(accountDatabaseCrawler);
|
||||
environment.lifecycle().manage(directoryReconciliationAccountDatabaseCrawler);
|
||||
environment.lifecycle().manage(accountCleanerAccountDatabaseCrawler);
|
||||
environment.lifecycle().manage(deletedAccountsTableCrawler);
|
||||
environment.lifecycle().manage(messagesCache);
|
||||
environment.lifecycle().manage(messagePersister);
|
||||
environment.lifecycle().manage(clientPresenceManager);
|
||||
environment.lifecycle().manage(currencyManager);
|
||||
environment.lifecycle().manage(directoryQueue);
|
||||
environment.lifecycle().manage(registrationServiceClient);
|
||||
|
||||
StaticCredentialsProvider cdnCredentialsProvider = StaticCredentialsProvider
|
||||
.create(AwsBasicCredentials.create(
|
||||
config.getCdnConfiguration().getAccessKey(),
|
||||
config.getCdnConfiguration().getAccessSecret()));
|
||||
S3Client cdnS3Client = S3Client.builder()
|
||||
.credentialsProvider(cdnCredentialsProvider)
|
||||
.region(Region.of(config.getCdnConfiguration().getRegion()))
|
||||
.build();
|
||||
PostPolicyGenerator profileCdnPolicyGenerator = new PostPolicyGenerator(config.getCdnConfiguration().getRegion(),
|
||||
config.getCdnConfiguration().getBucket(), config.getCdnConfiguration().getAccessKey());
|
||||
PolicySigner profileCdnPolicySigner = new PolicySigner(config.getCdnConfiguration().getAccessSecret(),
|
||||
config.getCdnConfiguration().getRegion());
|
||||
|
||||
ServerSecretParams zkSecretParams = new ServerSecretParams(config.getZkConfig().getServerSecret());
|
||||
ServerZkProfileOperations zkProfileOperations = new ServerZkProfileOperations(zkSecretParams);
|
||||
ServerZkAuthOperations zkAuthOperations = new ServerZkAuthOperations(zkSecretParams);
|
||||
ServerZkReceiptOperations zkReceiptOperations = new ServerZkReceiptOperations(zkSecretParams);
|
||||
|
||||
AuthFilter<BasicCredentials, AuthenticatedAccount> accountAuthFilter = new BasicCredentialAuthFilter.Builder<AuthenticatedAccount>().setAuthenticator(
|
||||
accountAuthenticator).buildAuthFilter();
|
||||
AuthFilter<BasicCredentials, DisabledPermittedAuthenticatedAccount> disabledPermittedAccountAuthFilter = new BasicCredentialAuthFilter.Builder<DisabledPermittedAuthenticatedAccount>().setAuthenticator(
|
||||
disabledPermittedAccountAuthenticator).buildAuthFilter();
|
||||
|
||||
environment.servlets()
|
||||
.addFilter("RemoteDeprecationFilter", new RemoteDeprecationFilter(dynamicConfigurationManager))
|
||||
.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), false, "/*");
|
||||
|
||||
environment.jersey().register(new RequestStatisticsFilter(TrafficSource.HTTP));
|
||||
environment.jersey().register(MultiRecipientMessageProvider.class);
|
||||
environment.jersey().register(new MetricsApplicationEventListener(TrafficSource.HTTP));
|
||||
environment.jersey()
|
||||
.register(new PolymorphicAuthDynamicFeature<>(ImmutableMap.of(AuthenticatedAccount.class, accountAuthFilter,
|
||||
DisabledPermittedAuthenticatedAccount.class, disabledPermittedAccountAuthFilter)));
|
||||
environment.jersey().register(new PolymorphicAuthValueFactoryProvider.Binder<>(
|
||||
ImmutableSet.of(AuthenticatedAccount.class, DisabledPermittedAuthenticatedAccount.class)));
|
||||
environment.jersey().register(new WebsocketRefreshApplicationEventListener(accountsManager, clientPresenceManager));
|
||||
environment.jersey().register(new TimestampResponseFilter());
|
||||
|
||||
///
|
||||
WebSocketEnvironment<AuthenticatedAccount> webSocketEnvironment = new WebSocketEnvironment<>(environment,
|
||||
config.getWebSocketConfiguration(), 90000);
|
||||
webSocketEnvironment.setAuthenticator(new WebSocketAccountAuthenticator(accountAuthenticator));
|
||||
webSocketEnvironment.setConnectListener(
|
||||
new AuthenticatedConnectListener(receiptSender, messagesManager, pushNotificationManager,
|
||||
clientPresenceManager, websocketScheduledExecutor));
|
||||
webSocketEnvironment.jersey()
|
||||
.register(new WebsocketRefreshApplicationEventListener(accountsManager, clientPresenceManager));
|
||||
webSocketEnvironment.jersey().register(new RequestStatisticsFilter(TrafficSource.WEBSOCKET));
|
||||
webSocketEnvironment.jersey().register(MultiRecipientMessageProvider.class);
|
||||
webSocketEnvironment.jersey().register(new MetricsApplicationEventListener(TrafficSource.WEBSOCKET));
|
||||
webSocketEnvironment.jersey().register(new KeepAliveController(clientPresenceManager));
|
||||
|
||||
// these should be common, but use @Auth DisabledPermittedAccount, which isn’t supported yet on websocket
|
||||
environment.jersey().register(
|
||||
new AccountController(pendingAccountsManager, accountsManager, rateLimiters,
|
||||
registrationServiceClient, dynamicConfigurationManager, turnTokenGenerator, config.getTestDevices(),
|
||||
captchaChecker, pushNotificationManager, changeNumberManager, registrationLockVerificationManager,
|
||||
registrationRecoveryPasswordsManager, usernameHashZkProofVerifier, clock));
|
||||
|
||||
environment.jersey().register(new KeysController(rateLimiters, keys, accountsManager));
|
||||
|
||||
boolean registeredSpamFilter = false;
|
||||
ReportSpamTokenProvider reportSpamTokenProvider = null;
|
||||
|
||||
for (final SpamFilter filter : ServiceLoader.load(SpamFilter.class)) {
|
||||
if (filter.getClass().isAnnotationPresent(FilterSpam.class)) {
|
||||
try {
|
||||
filter.configure(config.getSpamFilterConfiguration().getEnvironment());
|
||||
|
||||
ReportSpamTokenProvider thisProvider = filter.getReportSpamTokenProvider();
|
||||
if (reportSpamTokenProvider == null) {
|
||||
reportSpamTokenProvider = thisProvider;
|
||||
} else if (thisProvider != null) {
|
||||
log.info("Multiple spam report token providers found. Using the first.");
|
||||
}
|
||||
|
||||
filter.getReportedMessageListeners().forEach(reportMessageManager::addListener);
|
||||
|
||||
environment.lifecycle().manage(filter);
|
||||
environment.jersey().register(filter);
|
||||
webSocketEnvironment.jersey().register(filter);
|
||||
|
||||
log.info("Registered spam filter: {}", filter.getClass().getName());
|
||||
registeredSpamFilter = true;
|
||||
} catch (final Exception e) {
|
||||
log.warn("Failed to register spam filter: {}", filter.getClass().getName(), e);
|
||||
}
|
||||
} else {
|
||||
log.warn("Spam filter {} not annotated with @FilterSpam and will not be installed",
|
||||
filter.getClass().getName());
|
||||
}
|
||||
|
||||
if (filter instanceof RateLimitChallengeListener) {
|
||||
log.info("Registered rate limit challenge listener: {}", filter.getClass().getName());
|
||||
rateLimitChallengeManager.addListener((RateLimitChallengeListener) filter);
|
||||
}
|
||||
}
|
||||
|
||||
if (!registeredSpamFilter) {
|
||||
log.warn("No spam filters installed");
|
||||
}
|
||||
|
||||
if (reportSpamTokenProvider == null) {
|
||||
log.warn("No spam-reporting token providers found; using default (no-op) provider as a default");
|
||||
reportSpamTokenProvider = ReportSpamTokenProvider.noop();
|
||||
}
|
||||
|
||||
final List<Object> commonControllers = Lists.newArrayList(
|
||||
new AccountControllerV2(accountsManager, changeNumberManager, phoneVerificationTokenManager,
|
||||
registrationLockVerificationManager, rateLimiters),
|
||||
new ArtController(rateLimiters, artCredentialsGenerator),
|
||||
new AttachmentControllerV2(rateLimiters, config.getAwsAttachmentsConfiguration().getAccessKey(), config.getAwsAttachmentsConfiguration().getAccessSecret(), config.getAwsAttachmentsConfiguration().getRegion(), config.getAwsAttachmentsConfiguration().getBucket()),
|
||||
new AttachmentControllerV3(rateLimiters, config.getGcpAttachmentsConfiguration().getDomain(), config.getGcpAttachmentsConfiguration().getEmail(), config.getGcpAttachmentsConfiguration().getMaxSizeInBytes(), config.getGcpAttachmentsConfiguration().getPathPrefix(), config.getGcpAttachmentsConfiguration().getRsaSigningKey()),
|
||||
new CertificateController(new CertificateGenerator(config.getDeliveryCertificate().getCertificate(), config.getDeliveryCertificate().getPrivateKey(), config.getDeliveryCertificate().getExpiresDays()), zkAuthOperations, clock),
|
||||
new ChallengeController(rateLimitChallengeManager),
|
||||
new DeviceController(pendingDevicesManager, accountsManager, messagesManager, keys, rateLimiters, config.getMaxDevices()),
|
||||
new DirectoryController(directoryCredentialsGenerator),
|
||||
new DirectoryV2Controller(directoryV2CredentialsGenerator),
|
||||
new DonationController(clock, zkReceiptOperations, redeemedReceiptsManager, accountsManager, config.getBadges(),
|
||||
ReceiptCredentialPresentation::new),
|
||||
new MessageController(rateLimiters, messageSender, receiptSender, accountsManager, deletedAccountsManager, messagesManager, pushNotificationManager, reportMessageManager, multiRecipientMessageExecutor,
|
||||
reportSpamTokenProvider),
|
||||
new PaymentsController(currencyManager, paymentsCredentialsGenerator),
|
||||
new ProfileController(clock, rateLimiters, accountsManager, profilesManager, dynamicConfigurationManager,
|
||||
profileBadgeConverter, config.getBadges(), cdnS3Client, profileCdnPolicyGenerator, profileCdnPolicySigner,
|
||||
config.getCdnConfiguration().getBucket(), zkProfileOperations, batchIdentityCheckExecutor),
|
||||
new ProvisioningController(rateLimiters, provisioningManager),
|
||||
new RegistrationController(accountsManager, phoneVerificationTokenManager, registrationLockVerificationManager,
|
||||
rateLimiters),
|
||||
new RemoteConfigController(remoteConfigsManager, adminEventLogger,
|
||||
config.getRemoteConfigConfiguration().getAuthorizedTokens(),
|
||||
config.getRemoteConfigConfiguration().getGlobalConfig()),
|
||||
new SecureBackupController(backupCredentialsGenerator, accountsManager),
|
||||
new SecureStorageController(storageCredentialsGenerator),
|
||||
new SecureValueRecovery2Controller(svr2CredentialsGenerator),
|
||||
new StickerController(rateLimiters, config.getCdnConfiguration().getAccessKey(),
|
||||
config.getCdnConfiguration().getAccessSecret(), config.getCdnConfiguration().getRegion(),
|
||||
config.getCdnConfiguration().getBucket())
|
||||
);
|
||||
if (config.getSubscription() != null && config.getOneTimeDonations() != null) {
|
||||
commonControllers.add(new SubscriptionController(clock, config.getSubscription(), config.getOneTimeDonations(),
|
||||
subscriptionManager, stripeManager, braintreeManager, zkReceiptOperations, issuedReceiptsManager, profileBadgeConverter,
|
||||
resourceBundleLevelTranslator));
|
||||
}
|
||||
|
||||
for (Object controller : commonControllers) {
|
||||
environment.jersey().register(controller);
|
||||
webSocketEnvironment.jersey().register(controller);
|
||||
}
|
||||
|
||||
WebSocketEnvironment<AuthenticatedAccount> provisioningEnvironment = new WebSocketEnvironment<>(environment,
|
||||
webSocketEnvironment.getRequestLog(), 60000);
|
||||
provisioningEnvironment.jersey().register(new WebsocketRefreshApplicationEventListener(accountsManager, clientPresenceManager));
|
||||
provisioningEnvironment.setConnectListener(new ProvisioningConnectListener(pubSubManager));
|
||||
provisioningEnvironment.jersey().register(new MetricsApplicationEventListener(TrafficSource.WEBSOCKET));
|
||||
provisioningEnvironment.jersey().register(new KeepAliveController(clientPresenceManager));
|
||||
|
||||
registerCorsFilter(environment);
|
||||
registerExceptionMappers(environment, webSocketEnvironment, provisioningEnvironment);
|
||||
|
||||
environment.jersey().property(ServerProperties.UNWRAP_COMPLETION_STAGE_IN_WRITER_ENABLE, Boolean.TRUE);
|
||||
webSocketEnvironment.jersey().property(ServerProperties.UNWRAP_COMPLETION_STAGE_IN_WRITER_ENABLE, Boolean.TRUE);
|
||||
provisioningEnvironment.jersey().property(ServerProperties.UNWRAP_COMPLETION_STAGE_IN_WRITER_ENABLE, Boolean.TRUE);
|
||||
|
||||
WebSocketResourceProviderFactory<AuthenticatedAccount> webSocketServlet = new WebSocketResourceProviderFactory<>(
|
||||
webSocketEnvironment, AuthenticatedAccount.class, config.getWebSocketConfiguration());
|
||||
WebSocketResourceProviderFactory<AuthenticatedAccount> provisioningServlet = new WebSocketResourceProviderFactory<>(
|
||||
provisioningEnvironment, AuthenticatedAccount.class, config.getWebSocketConfiguration());
|
||||
|
||||
ServletRegistration.Dynamic websocket = environment.servlets().addServlet("WebSocket", webSocketServlet);
|
||||
ServletRegistration.Dynamic provisioning = environment.servlets().addServlet("Provisioning", provisioningServlet);
|
||||
|
||||
websocket.addMapping("/v1/websocket/");
|
||||
websocket.setAsyncSupported(true);
|
||||
|
||||
provisioning.addMapping("/v1/websocket/provisioning/");
|
||||
provisioning.setAsyncSupported(true);
|
||||
|
||||
environment.admin().addTask(new SetRequestLoggingEnabledTask());
|
||||
environment.admin().addTask(new SetCrawlerAccelerationTask(accountDatabaseCrawlerCache));
|
||||
|
||||
environment.healthChecks().register("cacheCluster", new RedisClusterHealthCheck(cacheCluster));
|
||||
|
||||
environment.lifecycle().manage(new ApplicationShutdownMonitor(Metrics.globalRegistry));
|
||||
|
||||
environment.metrics().register(name(CpuUsageGauge.class, "cpu"), new CpuUsageGauge(3, TimeUnit.SECONDS));
|
||||
environment.metrics().register(name(FreeMemoryGauge.class, "free_memory"), new FreeMemoryGauge());
|
||||
environment.metrics().register(name(NetworkSentGauge.class, "bytes_sent"), new NetworkSentGauge());
|
||||
environment.metrics().register(name(NetworkReceivedGauge.class, "bytes_received"), new NetworkReceivedGauge());
|
||||
environment.metrics().register(name(FileDescriptorGauge.class, "fd_count"), new FileDescriptorGauge());
|
||||
environment.metrics().register(name(MaxFileDescriptorGauge.class, "max_fd_count"), new MaxFileDescriptorGauge());
|
||||
environment.metrics()
|
||||
.register(name(OperatingSystemMemoryGauge.class, "buffers"), new OperatingSystemMemoryGauge("Buffers"));
|
||||
environment.metrics()
|
||||
.register(name(OperatingSystemMemoryGauge.class, "cached"), new OperatingSystemMemoryGauge("Cached"));
|
||||
|
||||
BufferPoolGauges.registerMetrics();
|
||||
GarbageCollectionGauges.registerMetrics();
|
||||
}
|
||||
|
||||
private void registerExceptionMappers(Environment environment,
|
||||
WebSocketEnvironment<AuthenticatedAccount> webSocketEnvironment,
|
||||
WebSocketEnvironment<AuthenticatedAccount> provisioningEnvironment) {
|
||||
|
||||
List.of(
|
||||
new LoggingUnhandledExceptionMapper(),
|
||||
new CompletionExceptionMapper(),
|
||||
new IOExceptionMapper(),
|
||||
new RateLimitExceededExceptionMapper(),
|
||||
new InvalidWebsocketAddressExceptionMapper(),
|
||||
new DeviceLimitExceededExceptionMapper(),
|
||||
new ServerRejectedExceptionMapper(),
|
||||
new ImpossiblePhoneNumberExceptionMapper(),
|
||||
new NonNormalizedPhoneNumberExceptionMapper(),
|
||||
new JsonMappingExceptionMapper()
|
||||
).forEach(exceptionMapper -> {
|
||||
environment.jersey().register(exceptionMapper);
|
||||
webSocketEnvironment.jersey().register(exceptionMapper);
|
||||
provisioningEnvironment.jersey().register(exceptionMapper);
|
||||
});
|
||||
}
|
||||
|
||||
private void registerCorsFilter(Environment environment) {
|
||||
FilterRegistration.Dynamic filter = environment.servlets().addFilter("CORS", CrossOriginFilter.class);
|
||||
filter.addMappingForUrlPatterns(EnumSet.allOf(DispatcherType.class), true, "/*");
|
||||
filter.setInitParameter("allowedOrigins", "*");
|
||||
filter.setInitParameter("allowedHeaders", "Content-Type,Authorization,X-Requested-With,Content-Length,Accept,Origin,X-Signal-Agent");
|
||||
filter.setInitParameter("allowedMethods", "GET,PUT,POST,DELETE,OPTIONS");
|
||||
filter.setInitParameter("preflightMaxAge", "5184000");
|
||||
filter.setInitParameter("allowCredentials", "true");
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
new WhisperServerService().run(args);
|
||||
}
|
||||
}
|
||||
@ -1,16 +0,0 @@
|
||||
/*
|
||||
* Copyright 2021 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.auth;
|
||||
|
||||
import org.whispersystems.textsecuregcm.storage.Account;
|
||||
import org.whispersystems.textsecuregcm.storage.Device;
|
||||
|
||||
public interface AccountAndAuthenticatedDeviceHolder {
|
||||
|
||||
Account getAccount();
|
||||
|
||||
Device getAuthenticatedDevice();
|
||||
}
|
||||
@ -1,25 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2021 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
package org.whispersystems.textsecuregcm.auth;
|
||||
|
||||
import io.dropwizard.auth.Authenticator;
|
||||
import io.dropwizard.auth.basic.BasicCredentials;
|
||||
import java.util.Optional;
|
||||
import org.whispersystems.textsecuregcm.experiment.ExperimentEnrollmentManager;
|
||||
import org.whispersystems.textsecuregcm.storage.AccountsManager;
|
||||
|
||||
public class AccountAuthenticator extends BaseAccountAuthenticator implements
|
||||
Authenticator<BasicCredentials, AuthenticatedAccount> {
|
||||
|
||||
public AccountAuthenticator(AccountsManager accountsManager) {
|
||||
super(accountsManager);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<AuthenticatedAccount> authenticate(BasicCredentials basicCredentials) {
|
||||
return super.authenticate(basicCredentials, true);
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,27 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2020 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.auth;
|
||||
|
||||
import java.util.Base64;
|
||||
import javax.ws.rs.WebApplicationException;
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
public class Anonymous {
|
||||
|
||||
private final byte[] unidentifiedSenderAccessKey;
|
||||
|
||||
public Anonymous(String header) {
|
||||
try {
|
||||
this.unidentifiedSenderAccessKey = Base64.getDecoder().decode(header);
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new WebApplicationException(e, Response.Status.UNAUTHORIZED);
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] getAccessKey() {
|
||||
return unidentifiedSenderAccessKey;
|
||||
}
|
||||
}
|
||||
@ -1,100 +0,0 @@
|
||||
/*
|
||||
* Copyright 2021 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.auth;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
import org.glassfish.jersey.server.ContainerRequest;
|
||||
import org.glassfish.jersey.server.monitoring.RequestEvent;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.whispersystems.textsecuregcm.storage.Account;
|
||||
import org.whispersystems.textsecuregcm.storage.AccountsManager;
|
||||
import org.whispersystems.textsecuregcm.storage.Device;
|
||||
import org.whispersystems.textsecuregcm.util.Pair;
|
||||
|
||||
/**
|
||||
* This {@link WebsocketRefreshRequirementProvider} observes intra-request changes in {@link Account#isEnabled()} and
|
||||
* {@link Device#isEnabled()}.
|
||||
* <p>
|
||||
* If a change in {@link Account#isEnabled()} or any associated {@link Device#isEnabled()} is observed, then any active
|
||||
* WebSocket connections for the account must be closed in order for clients to get a refreshed
|
||||
* {@link io.dropwizard.auth.Auth} object with a current device list.
|
||||
*
|
||||
* @see AuthenticatedAccount
|
||||
* @see DisabledPermittedAuthenticatedAccount
|
||||
*/
|
||||
public class AuthEnablementRefreshRequirementProvider implements WebsocketRefreshRequirementProvider {
|
||||
|
||||
private final AccountsManager accountsManager;
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(AuthEnablementRefreshRequirementProvider.class);
|
||||
|
||||
private static final String ACCOUNT_UUID = AuthEnablementRefreshRequirementProvider.class.getName() + ".accountUuid";
|
||||
private static final String DEVICES_ENABLED = AuthEnablementRefreshRequirementProvider.class.getName() + ".devicesEnabled";
|
||||
|
||||
public AuthEnablementRefreshRequirementProvider(final AccountsManager accountsManager) {
|
||||
this.accountsManager = accountsManager;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
static Map<Long, Boolean> buildDevicesEnabledMap(final Account account) {
|
||||
return account.getDevices().stream().collect(Collectors.toMap(Device::getId, Device::isEnabled));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleRequestFiltered(final RequestEvent requestEvent) {
|
||||
if (requestEvent.getUriInfo().getMatchedResourceMethod().getInvocable().getHandlingMethod().getAnnotation(ChangesDeviceEnabledState.class) != null) {
|
||||
// The authenticated principal, if any, will be available after filters have run.
|
||||
// Now that the account is known, capture a snapshot of `isEnabled` for the account's devices before carrying out
|
||||
// the request’s business logic.
|
||||
ContainerRequestUtil.getAuthenticatedAccount(requestEvent.getContainerRequest()).ifPresent(account ->
|
||||
setAccount(requestEvent.getContainerRequest(), account));
|
||||
}
|
||||
}
|
||||
|
||||
public static void setAccount(final ContainerRequest containerRequest, final Account account) {
|
||||
containerRequest.setProperty(ACCOUNT_UUID, account.getUuid());
|
||||
containerRequest.setProperty(DEVICES_ENABLED, buildDevicesEnabledMap(account));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Pair<UUID, Long>> handleRequestFinished(final RequestEvent requestEvent) {
|
||||
// Now that the request is finished, check whether `isEnabled` changed for any of the devices. If the value did
|
||||
// change or if a devices was added or removed, all devices must disconnect and reauthenticate.
|
||||
if (requestEvent.getContainerRequest().getProperty(DEVICES_ENABLED) != null) {
|
||||
|
||||
@SuppressWarnings("unchecked") final Map<Long, Boolean> initialDevicesEnabled =
|
||||
(Map<Long, Boolean>) requestEvent.getContainerRequest().getProperty(DEVICES_ENABLED);
|
||||
|
||||
return accountsManager.getByAccountIdentifier((UUID) requestEvent.getContainerRequest().getProperty(ACCOUNT_UUID)).map(account -> {
|
||||
final Set<Long> deviceIdsToDisplace;
|
||||
final Map<Long, Boolean> currentDevicesEnabled = buildDevicesEnabledMap(account);
|
||||
|
||||
if (!initialDevicesEnabled.equals(currentDevicesEnabled)) {
|
||||
deviceIdsToDisplace = new HashSet<>(initialDevicesEnabled.keySet());
|
||||
deviceIdsToDisplace.addAll(currentDevicesEnabled.keySet());
|
||||
} else {
|
||||
deviceIdsToDisplace = Collections.emptySet();
|
||||
}
|
||||
|
||||
return deviceIdsToDisplace.stream()
|
||||
.map(deviceId -> new Pair<>(account.getUuid(), deviceId))
|
||||
.collect(Collectors.toList());
|
||||
}).orElseGet(() -> {
|
||||
logger.error("Request had account, but it is no longer present");
|
||||
return Collections.emptyList();
|
||||
});
|
||||
} else
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
@ -1,44 +0,0 @@
|
||||
/*
|
||||
* Copyright 2021 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.auth;
|
||||
|
||||
import java.security.Principal;
|
||||
import java.util.function.Supplier;
|
||||
import javax.security.auth.Subject;
|
||||
import org.whispersystems.textsecuregcm.storage.Account;
|
||||
import org.whispersystems.textsecuregcm.storage.Device;
|
||||
import org.whispersystems.textsecuregcm.util.Pair;
|
||||
|
||||
public class AuthenticatedAccount implements Principal, AccountAndAuthenticatedDeviceHolder {
|
||||
|
||||
private final Supplier<Pair<Account, Device>> accountAndDevice;
|
||||
|
||||
public AuthenticatedAccount(final Supplier<Pair<Account, Device>> accountAndDevice) {
|
||||
this.accountAndDevice = accountAndDevice;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Account getAccount() {
|
||||
return accountAndDevice.get().first();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Device getAuthenticatedDevice() {
|
||||
return accountAndDevice.get().second();
|
||||
}
|
||||
|
||||
// Principal implementation
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean implies(final Subject subject) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -1,182 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.auth;
|
||||
|
||||
import static com.codahale.metrics.MetricRegistry.name;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import io.dropwizard.auth.basic.BasicCredentials;
|
||||
import io.micrometer.core.instrument.Metrics;
|
||||
import io.micrometer.core.instrument.Tags;
|
||||
import java.time.Clock;
|
||||
import java.time.Duration;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.whispersystems.textsecuregcm.storage.Account;
|
||||
import org.whispersystems.textsecuregcm.storage.AccountsManager;
|
||||
import org.whispersystems.textsecuregcm.storage.Device;
|
||||
import org.whispersystems.textsecuregcm.storage.RefreshingAccountAndDeviceSupplier;
|
||||
import org.whispersystems.textsecuregcm.util.Pair;
|
||||
import org.whispersystems.textsecuregcm.util.Util;
|
||||
|
||||
public class BaseAccountAuthenticator {
|
||||
|
||||
private static final String AUTHENTICATION_COUNTER_NAME = name(BaseAccountAuthenticator.class, "authentication");
|
||||
private static final String ENABLED_NOT_REQUIRED_AUTHENTICATION_COUNTER_NAME = name(BaseAccountAuthenticator.class,
|
||||
"enabledNotRequiredAuthentication");
|
||||
private static final String AUTHENTICATION_SUCCEEDED_TAG_NAME = "succeeded";
|
||||
private static final String AUTHENTICATION_FAILURE_REASON_TAG_NAME = "reason";
|
||||
private static final String ENABLED_TAG_NAME = "enabled";
|
||||
private static final String AUTHENTICATION_HAS_STORY_CAPABILITY = "hasStoryCapability";
|
||||
|
||||
private static final String STORY_ADOPTION_COUNTER_NAME = name(BaseAccountAuthenticator.class, "storyAdoption");
|
||||
|
||||
private static final String DAYS_SINCE_LAST_SEEN_DISTRIBUTION_NAME = name(BaseAccountAuthenticator.class, "daysSinceLastSeen");
|
||||
private static final String IS_PRIMARY_DEVICE_TAG = "isPrimary";
|
||||
|
||||
@VisibleForTesting
|
||||
static final char DEVICE_ID_SEPARATOR = '.';
|
||||
|
||||
private final AccountsManager accountsManager;
|
||||
private final Clock clock;
|
||||
|
||||
public BaseAccountAuthenticator(AccountsManager accountsManager) {
|
||||
this(accountsManager, Clock.systemUTC());
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public BaseAccountAuthenticator(AccountsManager accountsManager, Clock clock) {
|
||||
this.accountsManager = accountsManager;
|
||||
this.clock = clock;
|
||||
}
|
||||
|
||||
static Pair<String, Long> getIdentifierAndDeviceId(final String basicUsername) {
|
||||
final String identifier;
|
||||
final long deviceId;
|
||||
|
||||
final int deviceIdSeparatorIndex = basicUsername.indexOf(DEVICE_ID_SEPARATOR);
|
||||
|
||||
if (deviceIdSeparatorIndex == -1) {
|
||||
identifier = basicUsername;
|
||||
deviceId = Device.MASTER_ID;
|
||||
} else {
|
||||
identifier = basicUsername.substring(0, deviceIdSeparatorIndex);
|
||||
deviceId = Long.parseLong(basicUsername.substring(deviceIdSeparatorIndex + 1));
|
||||
}
|
||||
|
||||
return new Pair<>(identifier, deviceId);
|
||||
}
|
||||
|
||||
public Optional<AuthenticatedAccount> authenticate(BasicCredentials basicCredentials, boolean enabledRequired) {
|
||||
boolean succeeded = false;
|
||||
String failureReason = null;
|
||||
boolean hasStoryCapability = false;
|
||||
|
||||
try {
|
||||
final UUID accountUuid;
|
||||
final long deviceId;
|
||||
{
|
||||
final Pair<String, Long> identifierAndDeviceId = getIdentifierAndDeviceId(basicCredentials.getUsername());
|
||||
|
||||
accountUuid = UUID.fromString(identifierAndDeviceId.first());
|
||||
deviceId = identifierAndDeviceId.second();
|
||||
}
|
||||
|
||||
Optional<Account> account = accountsManager.getByAccountIdentifier(accountUuid);
|
||||
|
||||
if (account.isEmpty()) {
|
||||
failureReason = "noSuchAccount";
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
hasStoryCapability = account.map(Account::isStoriesSupported).orElse(false);
|
||||
|
||||
Optional<Device> device = account.get().getDevice(deviceId);
|
||||
|
||||
if (device.isEmpty()) {
|
||||
failureReason = "noSuchDevice";
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
if (enabledRequired) {
|
||||
final boolean deviceDisabled = !device.get().isEnabled();
|
||||
if (deviceDisabled) {
|
||||
failureReason = "deviceDisabled";
|
||||
}
|
||||
|
||||
final boolean accountDisabled = !account.get().isEnabled();
|
||||
if (accountDisabled) {
|
||||
failureReason = "accountDisabled";
|
||||
}
|
||||
if (accountDisabled || deviceDisabled) {
|
||||
return Optional.empty();
|
||||
}
|
||||
} else {
|
||||
Metrics.counter(ENABLED_NOT_REQUIRED_AUTHENTICATION_COUNTER_NAME,
|
||||
ENABLED_TAG_NAME, String.valueOf(device.get().isEnabled() && account.get().isEnabled()),
|
||||
IS_PRIMARY_DEVICE_TAG, String.valueOf(device.get().isMaster()))
|
||||
.increment();
|
||||
}
|
||||
|
||||
SaltedTokenHash deviceSaltedTokenHash = device.get().getAuthTokenHash();
|
||||
if (deviceSaltedTokenHash.verify(basicCredentials.getPassword())) {
|
||||
succeeded = true;
|
||||
Account authenticatedAccount = updateLastSeen(account.get(), device.get());
|
||||
if (deviceSaltedTokenHash.getVersion() != SaltedTokenHash.CURRENT_VERSION) {
|
||||
authenticatedAccount = accountsManager.updateDeviceAuthentication(
|
||||
authenticatedAccount,
|
||||
device.get(),
|
||||
SaltedTokenHash.generateFor(basicCredentials.getPassword())); // new credentials have current version
|
||||
}
|
||||
return Optional.of(new AuthenticatedAccount(
|
||||
new RefreshingAccountAndDeviceSupplier(authenticatedAccount, device.get().getId(), accountsManager)));
|
||||
}
|
||||
|
||||
return Optional.empty();
|
||||
} catch (IllegalArgumentException | InvalidAuthorizationHeaderException iae) {
|
||||
failureReason = "invalidHeader";
|
||||
return Optional.empty();
|
||||
} finally {
|
||||
Tags tags = Tags.of(
|
||||
AUTHENTICATION_SUCCEEDED_TAG_NAME, String.valueOf(succeeded));
|
||||
|
||||
if (StringUtils.isNotBlank(failureReason)) {
|
||||
tags = tags.and(AUTHENTICATION_FAILURE_REASON_TAG_NAME, failureReason);
|
||||
}
|
||||
|
||||
Metrics.counter(AUTHENTICATION_COUNTER_NAME, tags).increment();
|
||||
|
||||
Tags storyTags = Tags.of(AUTHENTICATION_HAS_STORY_CAPABILITY, String.valueOf(hasStoryCapability));
|
||||
Metrics.counter(STORY_ADOPTION_COUNTER_NAME, storyTags).increment();
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public Account updateLastSeen(Account account, Device device) {
|
||||
// compute a non-negative integer between 0 and 86400.
|
||||
long n = Util.ensureNonNegativeLong(account.getUuid().getLeastSignificantBits());
|
||||
final long lastSeenOffsetSeconds = n % ChronoUnit.DAYS.getDuration().toSeconds();
|
||||
|
||||
// produce a truncated timestamp which is either today at UTC midnight
|
||||
// or yesterday at UTC midnight, based on per-user randomized offset used.
|
||||
final long todayInMillisWithOffset = Util.todayInMillisGivenOffsetFromNow(clock, Duration.ofSeconds(lastSeenOffsetSeconds).negated());
|
||||
|
||||
// only update the device's last seen time when it falls behind the truncated timestamp.
|
||||
// this ensure a few things:
|
||||
// (1) each account will only update last-seen at most once per day
|
||||
// (2) these updates will occur throughout the day rather than all occurring at UTC midnight.
|
||||
if (device.getLastSeen() < todayInMillisWithOffset) {
|
||||
Metrics.summary(DAYS_SINCE_LAST_SEEN_DISTRIBUTION_NAME, IS_PRIMARY_DEVICE_TAG, String.valueOf(device.isMaster()))
|
||||
.record(Duration.ofMillis(todayInMillisWithOffset - device.getLastSeen()).toDays());
|
||||
|
||||
return accountsManager.updateDeviceLastSeen(account, device, Util.todayInMillis(clock));
|
||||
}
|
||||
|
||||
return account;
|
||||
}
|
||||
}
|
||||
@ -1,94 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2021 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
package org.whispersystems.textsecuregcm.auth;
|
||||
|
||||
import java.util.Base64;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.whispersystems.textsecuregcm.util.Pair;
|
||||
|
||||
public class BasicAuthorizationHeader {
|
||||
|
||||
private final String username;
|
||||
private final long deviceId;
|
||||
private final String password;
|
||||
|
||||
private BasicAuthorizationHeader(final String username, final long deviceId, final String password) {
|
||||
this.username = username;
|
||||
this.deviceId = deviceId;
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public static BasicAuthorizationHeader fromString(final String header) throws InvalidAuthorizationHeaderException {
|
||||
try {
|
||||
if (StringUtils.isBlank(header)) {
|
||||
throw new InvalidAuthorizationHeaderException("Blank header");
|
||||
}
|
||||
|
||||
final int spaceIndex = header.indexOf(' ');
|
||||
|
||||
if (spaceIndex == -1) {
|
||||
throw new InvalidAuthorizationHeaderException("Invalid authorization header: " + header);
|
||||
}
|
||||
|
||||
final String authorizationType = header.substring(0, spaceIndex);
|
||||
|
||||
if (!"Basic".equals(authorizationType)) {
|
||||
throw new InvalidAuthorizationHeaderException("Unsupported authorization method: " + authorizationType);
|
||||
}
|
||||
|
||||
final String credentials;
|
||||
|
||||
try {
|
||||
credentials = new String(Base64.getDecoder().decode(header.substring(spaceIndex + 1)));
|
||||
} catch (final IndexOutOfBoundsException e) {
|
||||
throw new InvalidAuthorizationHeaderException("Missing credentials");
|
||||
}
|
||||
|
||||
if (StringUtils.isEmpty(credentials)) {
|
||||
throw new InvalidAuthorizationHeaderException("Bad decoded value: " + credentials);
|
||||
}
|
||||
|
||||
final int credentialSeparatorIndex = credentials.indexOf(':');
|
||||
|
||||
if (credentialSeparatorIndex == -1) {
|
||||
throw new InvalidAuthorizationHeaderException("Badly-formatted credentials: " + credentials);
|
||||
}
|
||||
|
||||
final String usernameComponent = credentials.substring(0, credentialSeparatorIndex);
|
||||
|
||||
final String username;
|
||||
final long deviceId;
|
||||
{
|
||||
final Pair<String, Long> identifierAndDeviceId =
|
||||
BaseAccountAuthenticator.getIdentifierAndDeviceId(usernameComponent);
|
||||
|
||||
username = identifierAndDeviceId.first();
|
||||
deviceId = identifierAndDeviceId.second();
|
||||
}
|
||||
|
||||
final String password = credentials.substring(credentialSeparatorIndex + 1);
|
||||
|
||||
if (StringUtils.isAnyBlank(username, password)) {
|
||||
throw new InvalidAuthorizationHeaderException("Username or password were blank");
|
||||
}
|
||||
|
||||
return new BasicAuthorizationHeader(username, deviceId, password);
|
||||
} catch (final IllegalArgumentException | IndexOutOfBoundsException e) {
|
||||
throw new InvalidAuthorizationHeaderException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public long getDeviceId() {
|
||||
return deviceId;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
}
|
||||
@ -1,61 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2020 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.auth;
|
||||
|
||||
import com.google.protobuf.ByteString;
|
||||
import com.google.protobuf.InvalidProtocolBufferException;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.util.Base64;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.signal.libsignal.protocol.ecc.Curve;
|
||||
import org.signal.libsignal.protocol.ecc.ECPrivateKey;
|
||||
import org.whispersystems.textsecuregcm.entities.MessageProtos.SenderCertificate;
|
||||
import org.whispersystems.textsecuregcm.entities.MessageProtos.ServerCertificate;
|
||||
import org.whispersystems.textsecuregcm.storage.Account;
|
||||
import org.whispersystems.textsecuregcm.storage.Device;
|
||||
|
||||
public class CertificateGenerator {
|
||||
|
||||
private final ECPrivateKey privateKey;
|
||||
private final int expiresDays;
|
||||
private final ServerCertificate serverCertificate;
|
||||
|
||||
public CertificateGenerator(byte[] serverCertificate, ECPrivateKey privateKey, int expiresDays)
|
||||
throws InvalidProtocolBufferException
|
||||
{
|
||||
this.privateKey = privateKey;
|
||||
this.expiresDays = expiresDays;
|
||||
this.serverCertificate = ServerCertificate.parseFrom(serverCertificate);
|
||||
}
|
||||
|
||||
public byte[] createFor(Account account, Device device, boolean includeE164) throws InvalidKeyException {
|
||||
SenderCertificate.Certificate.Builder builder = SenderCertificate.Certificate.newBuilder()
|
||||
.setSenderDevice(Math.toIntExact(device.getId()))
|
||||
.setExpires(System.currentTimeMillis() + TimeUnit.DAYS.toMillis(expiresDays))
|
||||
.setIdentityKey(ByteString.copyFrom(Base64.getDecoder().decode(account.getIdentityKey())))
|
||||
.setSigner(serverCertificate)
|
||||
.setSenderUuid(account.getUuid().toString());
|
||||
|
||||
if (includeE164) {
|
||||
builder.setSender(account.getNumber());
|
||||
}
|
||||
|
||||
byte[] certificate = builder.build().toByteArray();
|
||||
byte[] signature;
|
||||
try {
|
||||
signature = Curve.calculateSignature(privateKey, certificate);
|
||||
} catch (org.signal.libsignal.protocol.InvalidKeyException e) {
|
||||
throw new InvalidKeyException(e);
|
||||
}
|
||||
|
||||
return SenderCertificate.newBuilder()
|
||||
.setCertificate(ByteString.copyFrom(certificate))
|
||||
.setSignature(ByteString.copyFrom(signature))
|
||||
.build()
|
||||
.toByteArray();
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,20 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2021 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.auth;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Indicates that an endpoint may change the "enabled" state of one or more devices associated with an account, and that
|
||||
* any websockets associated with the account may need to be refreshed after a call to that endpoint.
|
||||
*/
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface ChangesDeviceEnabledState {
|
||||
}
|
||||
@ -1,30 +0,0 @@
|
||||
/*
|
||||
* Copyright 2021 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.auth;
|
||||
|
||||
import java.util.Base64;
|
||||
import javax.ws.rs.WebApplicationException;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.Response.Status;
|
||||
|
||||
public class CombinedUnidentifiedSenderAccessKeys {
|
||||
private final byte[] combinedUnidentifiedSenderAccessKeys;
|
||||
|
||||
public CombinedUnidentifiedSenderAccessKeys(String header) {
|
||||
try {
|
||||
this.combinedUnidentifiedSenderAccessKeys = Base64.getDecoder().decode(header);
|
||||
if (this.combinedUnidentifiedSenderAccessKeys == null || this.combinedUnidentifiedSenderAccessKeys.length != 16) {
|
||||
throw new WebApplicationException("Invalid combined unidentified sender access keys", Status.UNAUTHORIZED);
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new WebApplicationException(e, Response.Status.UNAUTHORIZED);
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] getAccessKeys() {
|
||||
return combinedUnidentifiedSenderAccessKeys;
|
||||
}
|
||||
}
|
||||
@ -1,21 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2021 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.auth;
|
||||
|
||||
import org.glassfish.jersey.server.ContainerRequest;
|
||||
import org.whispersystems.textsecuregcm.storage.Account;
|
||||
import javax.ws.rs.core.SecurityContext;
|
||||
import java.util.Optional;
|
||||
|
||||
class ContainerRequestUtil {
|
||||
|
||||
static Optional<Account> getAuthenticatedAccount(final ContainerRequest request) {
|
||||
return Optional.ofNullable(request.getSecurityContext())
|
||||
.map(SecurityContext::getUserPrincipal)
|
||||
.map(principal -> principal instanceof AccountAndAuthenticatedDeviceHolder
|
||||
? ((AccountAndAuthenticatedDeviceHolder) principal).getAccount() : null);
|
||||
}
|
||||
}
|
||||
@ -1,26 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2021 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.auth;
|
||||
|
||||
import io.dropwizard.auth.Authenticator;
|
||||
import io.dropwizard.auth.basic.BasicCredentials;
|
||||
import java.util.Optional;
|
||||
import org.whispersystems.textsecuregcm.experiment.ExperimentEnrollmentManager;
|
||||
import org.whispersystems.textsecuregcm.storage.AccountsManager;
|
||||
|
||||
public class DisabledPermittedAccountAuthenticator extends BaseAccountAuthenticator implements
|
||||
Authenticator<BasicCredentials, DisabledPermittedAuthenticatedAccount> {
|
||||
|
||||
public DisabledPermittedAccountAuthenticator(AccountsManager accountsManager) {
|
||||
super(accountsManager);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<DisabledPermittedAuthenticatedAccount> authenticate(BasicCredentials credentials) {
|
||||
Optional<AuthenticatedAccount> account = super.authenticate(credentials, false);
|
||||
return account.map(DisabledPermittedAuthenticatedAccount::new);
|
||||
}
|
||||
}
|
||||
@ -1,42 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2021 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.auth;
|
||||
|
||||
import java.security.Principal;
|
||||
import javax.security.auth.Subject;
|
||||
import org.whispersystems.textsecuregcm.storage.Account;
|
||||
import org.whispersystems.textsecuregcm.storage.Device;
|
||||
|
||||
public class DisabledPermittedAuthenticatedAccount implements Principal, AccountAndAuthenticatedDeviceHolder {
|
||||
|
||||
private final AuthenticatedAccount authenticatedAccount;
|
||||
|
||||
public DisabledPermittedAuthenticatedAccount(final AuthenticatedAccount authenticatedAccount) {
|
||||
this.authenticatedAccount = authenticatedAccount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Account getAccount() {
|
||||
return authenticatedAccount.getAccount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Device getAuthenticatedDevice() {
|
||||
return authenticatedAccount.getAuthenticatedDevice();
|
||||
}
|
||||
|
||||
// Principal implementation
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean implies(Subject subject) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -1,11 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2020 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.auth;
|
||||
|
||||
|
||||
public record ExternalServiceCredentials(String username, String password) {
|
||||
|
||||
}
|
||||
@ -1,207 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.auth;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
import static org.whispersystems.textsecuregcm.util.HmacUtils.hmac256ToHexString;
|
||||
import static org.whispersystems.textsecuregcm.util.HmacUtils.hmac256TruncatedToHexString;
|
||||
import static org.whispersystems.textsecuregcm.util.HmacUtils.hmacHexStringsEqual;
|
||||
|
||||
import java.time.Clock;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
|
||||
public class ExternalServiceCredentialsGenerator {
|
||||
|
||||
private static final int TRUNCATE_LENGTH = 10;
|
||||
|
||||
private static final String DELIMITER = ":";
|
||||
|
||||
private final byte[] key;
|
||||
|
||||
private final byte[] userDerivationKey;
|
||||
|
||||
private final boolean prependUsername;
|
||||
|
||||
private final boolean truncateSignature;
|
||||
|
||||
private final Clock clock;
|
||||
|
||||
|
||||
public static ExternalServiceCredentialsGenerator.Builder builder(final byte[] key) {
|
||||
return new Builder(key);
|
||||
}
|
||||
|
||||
private ExternalServiceCredentialsGenerator(
|
||||
final byte[] key,
|
||||
final byte[] userDerivationKey,
|
||||
final boolean prependUsername,
|
||||
final boolean truncateSignature,
|
||||
final Clock clock) {
|
||||
this.key = requireNonNull(key);
|
||||
this.userDerivationKey = requireNonNull(userDerivationKey);
|
||||
this.prependUsername = prependUsername;
|
||||
this.truncateSignature = truncateSignature;
|
||||
this.clock = requireNonNull(clock);
|
||||
}
|
||||
|
||||
/**
|
||||
* A convenience method for the case of identity in the form of {@link UUID}.
|
||||
* @param uuid identity to generate credentials for
|
||||
* @return an instance of {@link ExternalServiceCredentials}
|
||||
*/
|
||||
public ExternalServiceCredentials generateForUuid(final UUID uuid) {
|
||||
return generateFor(uuid.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates `ExternalServiceCredentials` for the given identity following this generator's configuration.
|
||||
* @param identity identity string to generate credentials for
|
||||
* @return an instance of {@link ExternalServiceCredentials}
|
||||
*/
|
||||
public ExternalServiceCredentials generateFor(final String identity) {
|
||||
final String username = shouldDeriveUsername()
|
||||
? hmac256TruncatedToHexString(userDerivationKey, identity, TRUNCATE_LENGTH)
|
||||
: identity;
|
||||
|
||||
final long currentTimeSeconds = currentTimeSeconds();
|
||||
|
||||
final String dataToSign = username + DELIMITER + currentTimeSeconds;
|
||||
|
||||
final String signature = truncateSignature
|
||||
? hmac256TruncatedToHexString(key, dataToSign, TRUNCATE_LENGTH)
|
||||
: hmac256ToHexString(key, dataToSign);
|
||||
|
||||
final String token = (prependUsername ? dataToSign : currentTimeSeconds) + DELIMITER + signature;
|
||||
|
||||
return new ExternalServiceCredentials(username, token);
|
||||
}
|
||||
|
||||
/**
|
||||
* In certain cases, identity (as it was passed to `generateFor` method)
|
||||
* is a part of the signature (`password`, in terms of `ExternalServiceCredentials`) string itself.
|
||||
* For such cases, this method returns the value of the identity string.
|
||||
* @param password `password` part of `ExternalServiceCredentials`
|
||||
* @return non-empty optional with an identity string value, or empty if value can't be extracted.
|
||||
*/
|
||||
public Optional<String> identityFromSignature(final String password) {
|
||||
// for some generators, identity in the clear is just not a part of the password
|
||||
if (!prependUsername || shouldDeriveUsername() || StringUtils.isBlank(password)) {
|
||||
return Optional.empty();
|
||||
}
|
||||
// checking for the case of unexpected format
|
||||
return StringUtils.countMatches(password, DELIMITER) == 2
|
||||
? Optional.of(password.substring(0, password.indexOf(DELIMITER)))
|
||||
: Optional.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an instance of {@link ExternalServiceCredentials} object, checks that the password
|
||||
* matches the username taking into accound this generator's configuration.
|
||||
* @param credentials an instance of {@link ExternalServiceCredentials}
|
||||
* @return An optional with a timestamp (seconds) of when the credentials were generated,
|
||||
* or an empty optional if the password doesn't match the username for any reason (including malformed data)
|
||||
*/
|
||||
public Optional<Long> validateAndGetTimestamp(final ExternalServiceCredentials credentials) {
|
||||
final String[] parts = requireNonNull(credentials).password().split(DELIMITER);
|
||||
final String timestampSeconds;
|
||||
final String actualSignature;
|
||||
|
||||
// making sure password format matches our expectations based on the generator configuration
|
||||
if (parts.length == 3 && prependUsername) {
|
||||
final String username = parts[0];
|
||||
// username has to match the one from `credentials`
|
||||
if (!credentials.username().equals(username)) {
|
||||
return Optional.empty();
|
||||
}
|
||||
timestampSeconds = parts[1];
|
||||
actualSignature = parts[2];
|
||||
} else if (parts.length == 2 && !prependUsername) {
|
||||
timestampSeconds = parts[0];
|
||||
actualSignature = parts[1];
|
||||
} else {
|
||||
// unexpected password format
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
final String signedData = credentials.username() + DELIMITER + timestampSeconds;
|
||||
final String expectedSignature = truncateSignature
|
||||
? hmac256TruncatedToHexString(key, signedData, TRUNCATE_LENGTH)
|
||||
: hmac256ToHexString(key, signedData);
|
||||
|
||||
// if the signature is valid it's safe to parse the `timestampSeconds` string into Long
|
||||
return hmacHexStringsEqual(expectedSignature, actualSignature)
|
||||
? Optional.of(Long.valueOf(timestampSeconds))
|
||||
: Optional.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an instance of {@link ExternalServiceCredentials} object and the max allowed age for those credentials,
|
||||
* checks if credentials are valid and not expired.
|
||||
* @param credentials an instance of {@link ExternalServiceCredentials}
|
||||
* @param maxAgeSeconds age in seconds
|
||||
* @return An optional with a timestamp (seconds) of when the credentials were generated,
|
||||
* or an empty optional if the password doesn't match the username for any reason (including malformed data)
|
||||
*/
|
||||
public Optional<Long> validateAndGetTimestamp(final ExternalServiceCredentials credentials, final long maxAgeSeconds) {
|
||||
return validateAndGetTimestamp(credentials)
|
||||
.filter(ts -> currentTimeSeconds() - ts <= maxAgeSeconds);
|
||||
}
|
||||
|
||||
private boolean shouldDeriveUsername() {
|
||||
return userDerivationKey.length > 0;
|
||||
}
|
||||
|
||||
private long currentTimeSeconds() {
|
||||
return clock.instant().getEpochSecond();
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
|
||||
private final byte[] key;
|
||||
|
||||
private byte[] userDerivationKey = new byte[0];
|
||||
|
||||
private boolean prependUsername = true;
|
||||
|
||||
private boolean truncateSignature = true;
|
||||
|
||||
private Clock clock = Clock.systemUTC();
|
||||
|
||||
|
||||
private Builder(final byte[] key) {
|
||||
this.key = requireNonNull(key);
|
||||
}
|
||||
|
||||
public Builder withUserDerivationKey(final byte[] userDerivationKey) {
|
||||
Validate.isTrue(requireNonNull(userDerivationKey).length > 0, "userDerivationKey must not be empty");
|
||||
this.userDerivationKey = userDerivationKey;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder withClock(final Clock clock) {
|
||||
this.clock = requireNonNull(clock);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder prependUsername(final boolean prependUsername) {
|
||||
this.prependUsername = prependUsername;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder truncateSignature(final boolean truncateSignature) {
|
||||
this.truncateSignature = truncateSignature;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ExternalServiceCredentialsGenerator build() {
|
||||
return new ExternalServiceCredentialsGenerator(
|
||||
key, userDerivationKey, prependUsername, truncateSignature, clock);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,19 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2020 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
package org.whispersystems.textsecuregcm.auth;
|
||||
|
||||
|
||||
import javax.ws.rs.WebApplicationException;
|
||||
import javax.ws.rs.core.Response.Status;
|
||||
|
||||
public class InvalidAuthorizationHeaderException extends WebApplicationException {
|
||||
public InvalidAuthorizationHeaderException(String s) {
|
||||
super(s, Status.UNAUTHORIZED);
|
||||
}
|
||||
|
||||
public InvalidAuthorizationHeaderException(Exception e) {
|
||||
super(e, Status.UNAUTHORIZED);
|
||||
}
|
||||
}
|
||||
@ -1,80 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2020 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.auth;
|
||||
|
||||
import org.whispersystems.textsecuregcm.storage.Account;
|
||||
import org.whispersystems.textsecuregcm.storage.Device;
|
||||
|
||||
import javax.ws.rs.NotAuthorizedException;
|
||||
import javax.ws.rs.NotFoundException;
|
||||
import javax.ws.rs.WebApplicationException;
|
||||
import javax.ws.rs.core.Response;
|
||||
import java.security.MessageDigest;
|
||||
import java.util.Optional;
|
||||
|
||||
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
|
||||
public class OptionalAccess {
|
||||
|
||||
public static final String UNIDENTIFIED = "Unidentified-Access-Key";
|
||||
|
||||
public static void verify(Optional<Account> requestAccount,
|
||||
Optional<Anonymous> accessKey,
|
||||
Optional<Account> targetAccount,
|
||||
String deviceSelector)
|
||||
{
|
||||
try {
|
||||
verify(requestAccount, accessKey, targetAccount);
|
||||
|
||||
if (!deviceSelector.equals("*")) {
|
||||
long deviceId = Long.parseLong(deviceSelector);
|
||||
|
||||
Optional<Device> targetDevice = targetAccount.get().getDevice(deviceId);
|
||||
|
||||
if (targetDevice.isPresent() && targetDevice.get().isEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (requestAccount.isPresent()) {
|
||||
throw new NotFoundException();
|
||||
} else {
|
||||
throw new NotAuthorizedException(Response.Status.UNAUTHORIZED);
|
||||
}
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
throw new WebApplicationException(Response.status(422).build());
|
||||
}
|
||||
}
|
||||
|
||||
public static void verify(Optional<Account> requestAccount,
|
||||
Optional<Anonymous> accessKey,
|
||||
Optional<Account> targetAccount)
|
||||
{
|
||||
if (requestAccount.isPresent() && targetAccount.isPresent() && targetAccount.get().isEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
//noinspection ConstantConditions
|
||||
if (requestAccount.isPresent() && (targetAccount.isEmpty() || (targetAccount.isPresent() && !targetAccount.get().isEnabled()))) {
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
if (accessKey.isPresent() && targetAccount.isPresent() && targetAccount.get().isEnabled() && targetAccount.get().isUnrestrictedUnidentifiedAccess()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (accessKey.isPresent() &&
|
||||
targetAccount.isPresent() &&
|
||||
targetAccount.get().getUnidentifiedAccessKey().isPresent() &&
|
||||
targetAccount.get().isEnabled() &&
|
||||
MessageDigest.isEqual(accessKey.get().getAccessKey(), targetAccount.get().getUnidentifiedAccessKey().get()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
throw new NotAuthorizedException(Response.Status.UNAUTHORIZED);
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,46 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2021 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.auth;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
import org.glassfish.jersey.server.monitoring.RequestEvent;
|
||||
import org.whispersystems.textsecuregcm.storage.Account;
|
||||
import org.whispersystems.textsecuregcm.util.Pair;
|
||||
|
||||
public class PhoneNumberChangeRefreshRequirementProvider implements WebsocketRefreshRequirementProvider {
|
||||
|
||||
private static final String INITIAL_NUMBER_KEY =
|
||||
PhoneNumberChangeRefreshRequirementProvider.class.getName() + ".initialNumber";
|
||||
|
||||
@Override
|
||||
public void handleRequestFiltered(final RequestEvent requestEvent) {
|
||||
ContainerRequestUtil.getAuthenticatedAccount(requestEvent.getContainerRequest())
|
||||
.ifPresent(account -> requestEvent.getContainerRequest().setProperty(INITIAL_NUMBER_KEY, account.getNumber()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Pair<UUID, Long>> handleRequestFinished(final RequestEvent requestEvent) {
|
||||
final String initialNumber = (String) requestEvent.getContainerRequest().getProperty(INITIAL_NUMBER_KEY);
|
||||
|
||||
if (initialNumber != null) {
|
||||
final Optional<Account> maybeAuthenticatedAccount =
|
||||
ContainerRequestUtil.getAuthenticatedAccount(requestEvent.getContainerRequest());
|
||||
|
||||
return maybeAuthenticatedAccount
|
||||
.filter(account -> !initialNumber.equals(account.getNumber()))
|
||||
.map(account -> account.getDevices().stream()
|
||||
.map(device -> new Pair<>(account.getUuid(), device.getId()))
|
||||
.collect(Collectors.toList()))
|
||||
.orElse(Collections.emptyList());
|
||||
} else {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,98 +0,0 @@
|
||||
/*
|
||||
* Copyright 2023 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.auth;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
import java.time.Duration;
|
||||
import java.util.concurrent.CancellationException;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import javax.ws.rs.BadRequestException;
|
||||
import javax.ws.rs.ForbiddenException;
|
||||
import javax.ws.rs.NotAuthorizedException;
|
||||
import javax.ws.rs.ServerErrorException;
|
||||
import javax.ws.rs.core.Response;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.whispersystems.textsecuregcm.entities.PhoneVerificationRequest;
|
||||
import org.whispersystems.textsecuregcm.entities.RegistrationSession;
|
||||
import org.whispersystems.textsecuregcm.registration.RegistrationServiceClient;
|
||||
import org.whispersystems.textsecuregcm.storage.RegistrationRecoveryPasswordsManager;
|
||||
|
||||
public class PhoneVerificationTokenManager {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(PhoneVerificationTokenManager.class);
|
||||
private static final Duration REGISTRATION_RPC_TIMEOUT = Duration.ofSeconds(15);
|
||||
private static final long VERIFICATION_TIMEOUT_SECONDS = REGISTRATION_RPC_TIMEOUT.plusSeconds(1).getSeconds();
|
||||
|
||||
private final RegistrationServiceClient registrationServiceClient;
|
||||
private final RegistrationRecoveryPasswordsManager registrationRecoveryPasswordsManager;
|
||||
|
||||
public PhoneVerificationTokenManager(final RegistrationServiceClient registrationServiceClient,
|
||||
final RegistrationRecoveryPasswordsManager registrationRecoveryPasswordsManager) {
|
||||
this.registrationServiceClient = registrationServiceClient;
|
||||
this.registrationRecoveryPasswordsManager = registrationRecoveryPasswordsManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a {@link PhoneVerificationRequest} has a token that verifies the caller has confirmed access to the e164
|
||||
* number
|
||||
*
|
||||
* @param number the e164 presented for verification
|
||||
* @param request the request with exactly one verification token (RegistrationService sessionId or registration
|
||||
* recovery password)
|
||||
* @return if verification was successful, returns the verification type
|
||||
* @throws BadRequestException if the number does not match the sessionId’s number
|
||||
* @throws NotAuthorizedException if the session is not verified
|
||||
* @throws ForbiddenException if the recovery password is not valid
|
||||
* @throws InterruptedException if verification did not complete before a timeout
|
||||
*/
|
||||
public PhoneVerificationRequest.VerificationType verify(final String number, final PhoneVerificationRequest request)
|
||||
throws InterruptedException {
|
||||
|
||||
final PhoneVerificationRequest.VerificationType verificationType = request.verificationType();
|
||||
switch (verificationType) {
|
||||
case SESSION -> verifyBySessionId(number, request.decodeSessionId());
|
||||
case RECOVERY_PASSWORD -> verifyByRecoveryPassword(number, request.recoveryPassword());
|
||||
}
|
||||
|
||||
return verificationType;
|
||||
}
|
||||
|
||||
private void verifyBySessionId(final String number, final byte[] sessionId) throws InterruptedException {
|
||||
try {
|
||||
final RegistrationSession session = registrationServiceClient
|
||||
.getSession(sessionId, REGISTRATION_RPC_TIMEOUT)
|
||||
.get(VERIFICATION_TIMEOUT_SECONDS, TimeUnit.SECONDS)
|
||||
.orElseThrow(() -> new NotAuthorizedException("session not verified"));
|
||||
|
||||
if (!MessageDigest.isEqual(number.getBytes(), session.number().getBytes())) {
|
||||
throw new BadRequestException("number does not match session");
|
||||
}
|
||||
if (!session.verified()) {
|
||||
throw new NotAuthorizedException("session not verified");
|
||||
}
|
||||
} catch (final CancellationException | ExecutionException | TimeoutException e) {
|
||||
logger.error("Registration service failure", e);
|
||||
throw new ServerErrorException(Response.Status.SERVICE_UNAVAILABLE);
|
||||
}
|
||||
}
|
||||
|
||||
private void verifyByRecoveryPassword(final String number, final byte[] recoveryPassword)
|
||||
throws InterruptedException {
|
||||
try {
|
||||
final boolean verified = registrationRecoveryPasswordsManager.verify(number, recoveryPassword)
|
||||
.get(VERIFICATION_TIMEOUT_SECONDS, TimeUnit.SECONDS);
|
||||
if (!verified) {
|
||||
throw new ForbiddenException("recoveryPassword couldn't be verified");
|
||||
}
|
||||
} catch (final ExecutionException | TimeoutException e) {
|
||||
throw new ServerErrorException(Response.Status.SERVICE_UNAVAILABLE);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,103 +0,0 @@
|
||||
/*
|
||||
* Copyright 2023 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.auth;
|
||||
|
||||
import static org.whispersystems.textsecuregcm.metrics.MetricsUtil.name;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.ws.rs.WebApplicationException;
|
||||
import javax.ws.rs.core.Response;
|
||||
import org.whispersystems.textsecuregcm.controllers.RateLimitExceededException;
|
||||
import org.whispersystems.textsecuregcm.entities.RegistrationLockFailure;
|
||||
import org.whispersystems.textsecuregcm.limits.RateLimiters;
|
||||
import org.whispersystems.textsecuregcm.push.ClientPresenceManager;
|
||||
import org.whispersystems.textsecuregcm.storage.Account;
|
||||
import org.whispersystems.textsecuregcm.storage.AccountsManager;
|
||||
import org.whispersystems.textsecuregcm.util.Util;
|
||||
|
||||
public class RegistrationLockVerificationManager {
|
||||
|
||||
@VisibleForTesting
|
||||
public static final int FAILURE_HTTP_STATUS = 423;
|
||||
|
||||
private static final String LOCKED_ACCOUNT_COUNTER_NAME =
|
||||
name(RegistrationLockVerificationManager.class, "lockedAccount");
|
||||
private static final String LOCK_REASON_TAG_NAME = "lockReason";
|
||||
private static final String ALREADY_LOCKED_TAG_NAME = "alreadyLocked";
|
||||
|
||||
|
||||
private final AccountsManager accounts;
|
||||
private final ClientPresenceManager clientPresenceManager;
|
||||
private final ExternalServiceCredentialsGenerator backupServiceCredentialGenerator;
|
||||
private final RateLimiters rateLimiters;
|
||||
|
||||
public RegistrationLockVerificationManager(
|
||||
final AccountsManager accounts, final ClientPresenceManager clientPresenceManager,
|
||||
final ExternalServiceCredentialsGenerator backupServiceCredentialGenerator, final RateLimiters rateLimiters) {
|
||||
this.accounts = accounts;
|
||||
this.clientPresenceManager = clientPresenceManager;
|
||||
this.backupServiceCredentialGenerator = backupServiceCredentialGenerator;
|
||||
this.rateLimiters = rateLimiters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies the given registration lock credentials against the account’s current registration lock, if any
|
||||
*
|
||||
* @param account
|
||||
* @param clientRegistrationLock
|
||||
* @throws RateLimitExceededException
|
||||
* @throws WebApplicationException
|
||||
*/
|
||||
public void verifyRegistrationLock(final Account account, @Nullable final String clientRegistrationLock)
|
||||
throws RateLimitExceededException, WebApplicationException {
|
||||
|
||||
final StoredRegistrationLock existingRegistrationLock = account.getRegistrationLock();
|
||||
final ExternalServiceCredentials existingBackupCredentials =
|
||||
backupServiceCredentialGenerator.generateForUuid(account.getUuid());
|
||||
|
||||
if (!existingRegistrationLock.requiresClientRegistrationLock()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Util.isEmpty(clientRegistrationLock)) {
|
||||
rateLimiters.getPinLimiter().validate(account.getNumber());
|
||||
}
|
||||
|
||||
final String phoneNumber = account.getNumber();
|
||||
|
||||
if (!existingRegistrationLock.verify(clientRegistrationLock)) {
|
||||
// At this point, the client verified ownership of the phone number but doesn’t have the reglock PIN.
|
||||
// Freezing the existing account credentials will definitively start the reglock timeout.
|
||||
// Until the timeout, the current reglock can still be supplied,
|
||||
// along with phone number verification, to restore access.
|
||||
/*
|
||||
boolean alreadyLocked = existingAccount.hasLockedCredentials();
|
||||
Metrics.counter(LOCKED_ACCOUNT_COUNTER_NAME,
|
||||
LOCK_REASON_TAG_NAME, "verifiedNumberFailedReglock",
|
||||
ALREADY_LOCKED_TAG_NAME, Boolean.toString(alreadyLocked))
|
||||
.increment();
|
||||
|
||||
final Account updatedAccount;
|
||||
if (!alreadyLocked) {
|
||||
updatedAccount = accounts.update(existingAccount, Account::lockAuthenticationCredentials);
|
||||
} else {
|
||||
updatedAccount = existingAccount;
|
||||
}
|
||||
|
||||
List<Long> deviceIds = updatedAccount.getDevices().stream().map(Device::getId).toList();
|
||||
clientPresenceManager.disconnectAllPresences(updatedAccount.getUuid(), deviceIds);
|
||||
*/
|
||||
|
||||
throw new WebApplicationException(Response.status(FAILURE_HTTP_STATUS)
|
||||
.entity(new RegistrationLockFailure(existingRegistrationLock.getTimeRemaining(),
|
||||
existingRegistrationLock.needsFailureCredentials() ? existingBackupCredentials : null))
|
||||
.build());
|
||||
}
|
||||
|
||||
rateLimiters.getPinLimiter().clear(phoneNumber);
|
||||
}
|
||||
}
|
||||
@ -1,75 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
package org.whispersystems.textsecuregcm.auth;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.HexFormat;
|
||||
import org.signal.libsignal.protocol.kdf.HKDF;
|
||||
|
||||
public record SaltedTokenHash(String hash, String salt) {
|
||||
|
||||
public enum Version {
|
||||
V1,
|
||||
V2,
|
||||
}
|
||||
|
||||
public static final Version CURRENT_VERSION = Version.V2;
|
||||
|
||||
private static final String V2_PREFIX = "2.";
|
||||
|
||||
private static final byte[] AUTH_TOKEN_HKDF_INFO = "authtoken".getBytes(StandardCharsets.UTF_8);
|
||||
|
||||
private static final int SALT_SIZE = 16;
|
||||
|
||||
private static final SecureRandom SECURE_RANDOM = new SecureRandom();
|
||||
|
||||
|
||||
public static SaltedTokenHash generateFor(final String token) {
|
||||
final String salt = generateSalt();
|
||||
final String hash = calculateV2Hash(salt, token);
|
||||
return new SaltedTokenHash(hash, salt);
|
||||
}
|
||||
|
||||
public Version getVersion() {
|
||||
return hash.startsWith(V2_PREFIX) ? Version.V2 : Version.V1;
|
||||
}
|
||||
|
||||
public boolean verify(final String token) {
|
||||
final String theirValue = switch (getVersion()) {
|
||||
case V1 -> calculateV1Hash(salt, token);
|
||||
case V2 -> calculateV2Hash(salt, token);
|
||||
};
|
||||
return MessageDigest.isEqual(
|
||||
theirValue.getBytes(StandardCharsets.UTF_8),
|
||||
hash.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
private static String generateSalt() {
|
||||
final byte[] salt = new byte[SALT_SIZE];
|
||||
SECURE_RANDOM.nextBytes(salt);
|
||||
return HexFormat.of().formatHex(salt);
|
||||
}
|
||||
|
||||
private static String calculateV1Hash(final String salt, final String token) {
|
||||
try {
|
||||
return HexFormat.of()
|
||||
.formatHex(MessageDigest.getInstance("SHA1").digest((salt + token).getBytes(StandardCharsets.UTF_8)));
|
||||
} catch (final NoSuchAlgorithmException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static String calculateV2Hash(final String salt, final String token) {
|
||||
final byte[] secret = HKDF.deriveSecrets(
|
||||
token.getBytes(StandardCharsets.UTF_8), // key
|
||||
salt.getBytes(StandardCharsets.UTF_8), // salt
|
||||
AUTH_TOKEN_HKDF_INFO,
|
||||
32);
|
||||
return V2_PREFIX + HexFormat.of().formatHex(secret);
|
||||
}
|
||||
}
|
||||
@ -1,69 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.auth;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import javax.annotation.Nullable;
|
||||
import org.whispersystems.textsecuregcm.util.Util;
|
||||
|
||||
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
|
||||
public class StoredRegistrationLock {
|
||||
|
||||
private final Optional<String> registrationLock;
|
||||
|
||||
private final Optional<String> registrationLockSalt;
|
||||
|
||||
private final long lastSeen;
|
||||
|
||||
/**
|
||||
* @return milliseconds since the last time the account was seen.
|
||||
*/
|
||||
private long timeSinceLastSeen() {
|
||||
return System.currentTimeMillis() - lastSeen;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if the registration lock and salt are both set.
|
||||
*/
|
||||
private boolean hasLockAndSalt() {
|
||||
return registrationLock.isPresent() && registrationLockSalt.isPresent();
|
||||
}
|
||||
|
||||
public StoredRegistrationLock(Optional<String> registrationLock, Optional<String> registrationLockSalt, long lastSeen) {
|
||||
this.registrationLock = registrationLock;
|
||||
this.registrationLockSalt = registrationLockSalt;
|
||||
this.lastSeen = lastSeen;
|
||||
}
|
||||
|
||||
public boolean requiresClientRegistrationLock() {
|
||||
boolean hasTimeRemaining = getTimeRemaining() >= 0;
|
||||
return hasLockAndSalt() && hasTimeRemaining;
|
||||
}
|
||||
|
||||
public boolean needsFailureCredentials() {
|
||||
return hasLockAndSalt();
|
||||
}
|
||||
|
||||
public long getTimeRemaining() {
|
||||
return TimeUnit.DAYS.toMillis(7) - timeSinceLastSeen();
|
||||
}
|
||||
|
||||
public boolean verify(@Nullable String clientRegistrationLock) {
|
||||
if (hasLockAndSalt() && Util.nonEmpty(clientRegistrationLock)) {
|
||||
SaltedTokenHash credentials = new SaltedTokenHash(registrationLock.get(), registrationLockSalt.get());
|
||||
return credentials.verify(clientRegistrationLock);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public StoredRegistrationLock forTime(long timestamp) {
|
||||
return new StoredRegistrationLock(registrationLock, registrationLockSalt, timestamp);
|
||||
}
|
||||
}
|
||||
@ -1,30 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2020 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.auth;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
import java.time.Duration;
|
||||
import javax.annotation.Nullable;
|
||||
import org.whispersystems.textsecuregcm.util.Util;
|
||||
|
||||
public record StoredVerificationCode(String code,
|
||||
long timestamp,
|
||||
String pushCode,
|
||||
@Nullable byte[] sessionId) {
|
||||
|
||||
public static final Duration EXPIRATION = Duration.ofMinutes(10);
|
||||
|
||||
public boolean isValid(String theirCodeString) {
|
||||
if (Util.isEmpty(code) || Util.isEmpty(theirCodeString)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
byte[] ourCode = code.getBytes();
|
||||
byte[] theirCode = theirCodeString.getBytes();
|
||||
|
||||
return MessageDigest.isEqual(ourCode, theirCode);
|
||||
}
|
||||
}
|
||||
@ -1,34 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2020 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.auth;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class TurnToken {
|
||||
|
||||
@JsonProperty
|
||||
private String username;
|
||||
|
||||
@JsonProperty
|
||||
private String password;
|
||||
|
||||
@JsonProperty
|
||||
private List<String> urls;
|
||||
|
||||
public TurnToken(String username, String password, List<String> urls) {
|
||||
this.username = username;
|
||||
this.password = password;
|
||||
this.urls = urls;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
List<String> getUrls() {
|
||||
return urls;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user