Compare commits

..

356 Commits

Author SHA1 Message Date
Documentation Updater
af4397d00e Updating documentation 2026-06-25 22:07:05 +00:00
Documentation Updater
87cd46d42f Updating documentation 2026-06-23 21:13:57 +00:00
Documentation Updater
05e38de854 Updating documentation 2026-06-18 19:40:17 +00:00
Documentation Updater
4d57da79ac Updating documentation 2026-06-18 15:07:21 +00:00
Documentation Updater
8309273cdb Updating documentation 2026-06-16 13:51:53 +00:00
Documentation Updater
394467b694 Updating documentation 2026-06-11 18:18:11 +00:00
Documentation Updater
251fa186dc Updating documentation 2026-06-04 13:25:59 +00:00
Documentation Updater
45fab90a00 Updating documentation 2026-06-02 20:36:54 +00:00
Documentation Updater
5d2ff7fd3b Updating documentation 2026-05-14 17:10:26 +00:00
Documentation Updater
f7b0357563 Updating documentation 2026-04-29 18:13:05 +00:00
Documentation Updater
75dabfcd86 Updating documentation 2026-04-17 18:11:56 +00:00
Documentation Updater
fa0efcaebb Updating documentation 2026-04-17 15:49:37 +00:00
Documentation Updater
7c2d482afe Updating documentation 2026-04-15 22:06:16 +00:00
Documentation Updater
624e45ec8f Updating documentation 2026-04-14 16:03:59 +00:00
Documentation Updater
8d0a494bee Updating documentation 2026-04-14 14:54:00 +00:00
Documentation Updater
cb0dae7ae1 Updating documentation 2026-04-10 00:41:51 +00:00
Documentation Updater
53c520c1ef Updating documentation 2026-04-02 00:57:28 +00:00
Documentation Updater
662ceda87b Updating documentation 2026-04-01 20:32:18 +00:00
Documentation Updater
cbe51139f9 Updating documentation 2026-04-01 17:56:56 +00:00
Documentation Updater
252666b020 Updating documentation 2026-03-31 17:02:57 +00:00
Documentation Updater
2d78b484a4 Updating documentation 2026-03-30 17:36:08 +00:00
Documentation Updater
1155ad62ba Updating documentation 2026-03-24 20:21:57 +00:00
Documentation Updater
8239f0ec89 Updating documentation 2026-03-19 18:45:16 +00:00
Documentation Updater
89036535c7 Updating documentation 2026-03-18 14:49:39 +00:00
Documentation Updater
aadf492a17 Updating documentation 2026-03-17 18:42:04 +00:00
Documentation Updater
49ac4472c4 Updating documentation 2026-03-16 14:33:30 +00:00
Documentation Updater
6fa4d87ec2 Updating documentation 2026-03-13 23:26:25 +00:00
Documentation Updater
229d1bd89c Updating documentation 2026-03-12 22:34:17 +00:00
Documentation Updater
6bde704cc1 Updating documentation 2026-03-12 16:49:36 +00:00
Documentation Updater
ddc0649111 Updating documentation 2026-03-12 16:13:38 +00:00
Documentation Updater
de80d7a3c1 Updating documentation 2026-03-11 18:33:39 +00:00
Documentation Updater
19ca8bf94d Updating documentation 2026-03-06 21:56:28 +00:00
Documentation Updater
c4008500da Updating documentation 2026-03-06 17:47:01 +00:00
Documentation Updater
b6b543d09b Updating documentation 2026-03-03 00:20:38 +00:00
Documentation Updater
ca98e0c45a Updating documentation 2026-02-27 22:49:39 +00:00
Documentation Updater
b672a344e3 Updating documentation 2026-02-24 22:57:28 +00:00
Documentation Updater
29c4694842 Updating documentation 2026-02-17 21:53:40 +00:00
Documentation Updater
22e449dc81 Updating documentation 2026-01-30 20:31:03 +00:00
Documentation Updater
020af68951 Updating documentation 2026-01-29 18:29:03 +00:00
Documentation Updater
8939188dac Updating documentation 2026-01-28 23:59:43 +00:00
Documentation Updater
8ec6cd5ae0 Updating documentation 2026-01-23 22:08:20 +00:00
Documentation Updater
7373d1da32 Updating documentation 2026-01-23 21:37:34 +00:00
Documentation Updater
847ecdb24f Updating documentation 2026-01-22 19:17:14 +00:00
Documentation Updater
f5af61304c Updating documentation 2026-01-13 21:04:51 +00:00
Documentation Updater
4891ef94e2 Updating documentation 2026-01-13 21:02:52 +00:00
Documentation Updater
fb865d9c6c Updating documentation 2026-01-10 16:57:42 +00:00
Documentation Updater
00750c1c41 Updating documentation 2025-12-10 23:28:55 +00:00
Documentation Updater
11bd3ce19c Updating documentation 2025-12-10 16:44:16 +00:00
Documentation Updater
de8d93bfb4 Updating documentation 2025-12-10 15:51:59 +00:00
Documentation Updater
ae190638d8 Updating documentation 2025-12-03 22:17:55 +00:00
Documentation Updater
afaa10b176 Updating documentation 2025-12-02 21:35:45 +00:00
Documentation Updater
d42e45b43c Updating documentation 2025-11-18 19:26:47 +00:00
Documentation Updater
4d464a762a Updating documentation 2025-10-30 15:34:18 +00:00
Documentation Updater
ffd1c80885 Updating documentation 2025-10-24 20:09:56 +00:00
Documentation Updater
bfbfe93af0 Updating documentation 2025-10-16 20:32:43 +00:00
Documentation Updater
f6f38e2fbd Updating documentation 2025-10-06 19:17:37 +00:00
Documentation Updater
2473ac8e62 Updating documentation 2025-10-01 18:42:55 +00:00
Documentation Updater
30e65782a2 Updating documentation 2025-09-17 15:59:49 +00:00
Documentation Updater
4c9a1be489 Updating documentation 2025-09-12 22:38:09 +00:00
Documentation Updater
d53f1dc247 Updating documentation 2025-09-02 20:41:53 +00:00
Documentation Updater
772688cc9a Updating documentation 2025-08-04 19:13:23 +00:00
Documentation Updater
813e390a6e Updating documentation 2025-08-04 14:34:02 +00:00
Documentation Updater
358795ca77 Updating documentation 2025-08-01 18:01:50 +00:00
Documentation Updater
5d145d020f Updating documentation 2025-07-30 20:33:11 +00:00
Documentation Updater
63db4a6770 Updating documentation 2025-07-29 16:25:24 +00:00
Documentation Updater
62d91a3f25 Updating documentation 2025-07-23 16:19:55 +00:00
Documentation Updater
dbfaa1187d Updating documentation 2025-07-21 18:24:40 +00:00
Documentation Updater
9e131743c5 Updating documentation 2025-07-18 23:14:57 +00:00
Documentation Updater
2d3de3a988 Updating documentation 2025-07-16 23:32:35 +00:00
Documentation Updater
bec414bd5c Updating documentation 2025-07-16 17:41:07 +00:00
Documentation Updater
8b5f8bf427 Updating documentation 2025-07-15 22:09:12 +00:00
Documentation Updater
69a14de876 Updating documentation 2025-07-11 19:51:09 +00:00
Documentation Updater
0f5256b758 Updating documentation 2025-07-09 15:28:32 +00:00
Documentation Updater
1fa8ece0b0 Updating documentation 2025-07-07 16:57:19 +00:00
Documentation Updater
f3256f8c29 Updating documentation 2025-06-23 14:50:57 +00:00
Documentation Updater
2dba844a59 Updating documentation 2025-06-18 19:49:28 +00:00
Documentation Updater
66d53cf6f0 Updating documentation 2025-06-17 15:42:10 +00:00
Documentation Updater
2eab162f8c Updating documentation 2025-06-10 23:19:02 +00:00
Documentation Updater
0feab732e3 Updating documentation 2025-06-04 18:23:19 +00:00
Documentation Updater
7e7e3a5c25 Updating documentation 2025-05-30 00:11:05 +00:00
Documentation Updater
61aeb30a8a Updating documentation 2025-05-28 21:51:33 +00:00
Documentation Updater
50901473db Updating documentation 2025-05-15 16:19:11 +00:00
Documentation Updater
b95564ccc4 Updating documentation 2025-04-28 19:32:18 +00:00
Documentation Updater
633755e3e5 Updating documentation 2025-04-25 16:01:42 +00:00
Documentation Updater
55485ed6f6 Updating documentation 2025-04-24 17:47:35 +00:00
Documentation Updater
898e9de371 Updating documentation 2025-04-17 16:23:11 +00:00
Documentation Updater
52399585a6 Updating documentation 2025-04-10 02:10:39 +00:00
Documentation Updater
666850cc2b Updating documentation 2025-04-09 18:49:41 +00:00
Documentation Updater
45987d101b Updating documentation 2025-04-08 15:44:54 +00:00
Documentation Updater
131af1a147 Updating documentation 2025-04-07 16:05:03 +00:00
Documentation Updater
4db5e34305 Updating documentation 2025-04-07 14:04:42 +00:00
Documentation Updater
e1bc6c18d2 Updating documentation 2025-04-02 20:14:08 +00:00
Documentation Updater
4250869835 Updating documentation 2025-04-02 13:44:30 +00:00
Documentation Updater
4e2fcfce5f Updating documentation 2025-03-27 17:22:32 +00:00
Documentation Updater
edfc25832b Updating documentation 2025-03-24 18:44:00 +00:00
Documentation Updater
bbdcb82e86 Updating documentation 2025-03-19 22:10:06 +00:00
Documentation Updater
253ea3a5ba Updating documentation 2025-03-08 17:47:54 +00:00
Documentation Updater
a618c44f4b Updating documentation 2025-02-24 22:15:17 +00:00
Documentation Updater
2407391557 Updating documentation 2025-02-11 00:13:30 +00:00
Documentation Updater
3e1d117b68 Updating documentation 2025-02-10 17:48:55 +00:00
Documentation Updater
6f7bbe3d77 Updating documentation 2025-02-07 20:42:24 +00:00
Documentation Updater
0c1e7d842c Updating documentation 2025-02-03 22:29:28 +00:00
Documentation Updater
c92559c7cf Updating documentation 2025-01-31 16:57:46 +00:00
Documentation Updater
b13b94bc99 Updating documentation 2025-01-14 21:42:03 +00:00
Documentation Updater
138316ab33 Updating documentation 2025-01-10 15:58:03 +00:00
Documentation Updater
9e603380fb Updating documentation 2025-01-03 15:43:50 +00:00
Documentation Updater
85d2ea9eef Updating documentation 2024-12-27 23:50:47 +00:00
Documentation Updater
1d63384de9 Updating documentation 2024-12-20 19:27:04 +00:00
Documentation Updater
c7d628ee62 Updating documentation 2024-12-19 17:12:22 +00:00
Documentation Updater
6662ce651b Updating documentation 2024-12-17 20:57:18 +00:00
Documentation Updater
9366e19014 Updating documentation 2024-12-12 23:16:37 +00:00
Documentation Updater
7af2433167 Updating documentation 2024-12-04 17:55:33 +00:00
Documentation Updater
6fa6b64d9c Updating documentation 2024-11-27 17:49:17 +00:00
Documentation Updater
3da371107d Updating documentation 2024-11-27 00:54:04 +00:00
Documentation Updater
d953f5cde3 Updating documentation 2024-11-26 23:24:32 +00:00
Documentation Updater
36d86e83cf Updating documentation 2024-11-25 21:40:54 +00:00
Documentation Updater
238f896837 Updating documentation 2024-11-23 00:09:55 +00:00
Documentation Updater
605dcb95db Updating documentation 2024-11-22 21:16:07 +00:00
Documentation Updater
7ac354918d Updating documentation 2024-11-15 21:01:07 +00:00
Documentation Updater
6bdda194ff Updating documentation 2024-11-12 17:58:45 +00:00
Documentation Updater
837c97a15a Updating documentation 2024-11-11 17:43:25 +00:00
Documentation Updater
c03b35cc5a Updating documentation 2024-11-06 21:08:01 +00:00
Documentation Updater
c3213b6d39 Updating documentation 2024-11-06 19:26:30 +00:00
Documentation Updater
eeec613888 Updating documentation 2024-11-06 15:17:41 +00:00
Documentation Updater
ff83f19ee9 Updating documentation 2024-11-05 17:02:16 +00:00
Documentation Updater
17f72be3ea Updating documentation 2024-11-01 21:14:22 +00:00
Documentation Updater
f0ba2c9804 Updating documentation 2024-10-31 22:35:50 +00:00
Documentation Updater
e27c12b3a0 Updating documentation 2024-10-31 15:13:49 +00:00
Documentation Updater
fbe5a0babb Updating documentation 2024-10-30 21:18:44 +00:00
Documentation Updater
d75cf059c0 Updating documentation 2024-10-29 20:46:38 +00:00
Documentation Updater
06b039dac4 Updating documentation 2024-10-29 14:58:52 +00:00
Documentation Updater
c99691fca5 Updating documentation 2024-10-24 19:26:37 +00:00
Documentation Updater
baffa0703d Updating documentation 2024-10-23 21:45:59 +00:00
Documentation Updater
78e76338c8 Updating documentation 2024-10-21 19:48:25 +00:00
Documentation Updater
d7e42ae922 Updating documentation 2024-10-17 17:48:49 +00:00
Documentation Updater
420e1d089c Updating documentation 2024-10-15 17:24:29 +00:00
Documentation Updater
1bdbe67a54 Updating documentation 2024-10-10 22:00:09 +00:00
Documentation Updater
39dda7c410 Updating documentation 2024-10-10 15:08:34 +00:00
Documentation Updater
18d1069418 Updating documentation 2024-10-09 15:30:44 +00:00
Documentation Updater
e7c54b3225 Updating documentation 2024-10-04 22:17:59 +00:00
Documentation Updater
9f89e84b58 Updating documentation 2024-10-01 19:50:52 +00:00
Documentation Updater
68b7d824ab Updating documentation 2024-09-27 22:00:09 +00:00
Documentation Updater
e75e38271b Updating documentation 2024-09-26 23:34:16 +00:00
Documentation Updater
1834229177 Updating documentation 2024-09-24 23:34:35 +00:00
Documentation Updater
52bbdbd953 Updating documentation 2024-09-23 20:49:17 +00:00
Documentation Updater
309dd31165 Updating documentation 2024-09-19 00:23:41 +00:00
Documentation Updater
96ab308e6c Updating documentation 2024-09-17 21:23:37 +00:00
Documentation Updater
b2b4e20817 Updating documentation 2024-09-16 19:46:49 +00:00
Documentation Updater
d9d91ed53e Updating documentation 2024-09-13 21:08:03 +00:00
Documentation Updater
bad3a266fb Updating documentation 2024-09-06 18:40:57 +00:00
Documentation Updater
eb97cb07c0 Updating documentation 2024-08-30 19:18:07 +00:00
Documentation Updater
1dd2050c47 Updating documentation 2024-08-28 22:01:56 +00:00
Documentation Updater
578e9b6501 Updating documentation 2024-08-21 22:56:29 +00:00
Documentation Updater
48ca510fb2 Updating documentation 2024-08-16 21:27:40 +00:00
Documentation Updater
4ecb26203c Updating documentation 2024-08-16 00:10:28 +00:00
Documentation Updater
465e8b8449 Updating documentation 2024-08-14 17:19:54 +00:00
Documentation Updater
d9a89c26f9 Updating documentation 2024-08-12 22:58:14 +00:00
Documentation Updater
99a12d3e76 Updating documentation 2024-08-12 22:19:31 +00:00
Documentation Updater
1e07ef8daa Updating documentation 2024-08-07 21:14:33 +00:00
Documentation Updater
6f5fc324ce Updating documentation 2024-08-06 22:00:13 +00:00
Documentation Updater
749a7a43b4 Updating documentation 2024-08-02 21:30:15 +00:00
Documentation Updater
0bf04a27fa Updating documentation 2024-07-25 16:01:13 +00:00
Documentation Updater
1ee2032a38 Updating documentation 2024-07-24 15:08:10 +00:00
Documentation Updater
25ebf298a0 Updating documentation 2024-07-23 22:28:22 +00:00
Documentation Updater
da2727bd42 Updating documentation 2024-07-18 18:57:14 +00:00
Documentation Updater
ee8936c96a Updating documentation 2024-07-17 20:36:43 +00:00
Documentation Updater
4f2018d416 Updating documentation 2024-07-08 21:23:47 +00:00
Documentation Updater
bd29d93293 Updating documentation 2024-06-25 15:49:45 +00:00
Documentation Updater
f1c599a166 Updating documentation 2024-06-24 20:06:40 +00:00
Documentation Updater
f4d35aefb7 Updating documentation 2024-06-20 22:08:49 +00:00
Documentation Updater
135363068f Updating documentation 2024-06-20 15:56:38 +00:00
Documentation Updater
8e8bc2b827 Updating documentation 2024-06-14 14:46:37 +00:00
Documentation Updater
f916e61533 Updating documentation 2024-06-14 14:35:32 +00:00
Documentation Updater
b05f40c33d Updating documentation 2024-06-12 19:07:29 +00:00
Documentation Updater
05ec2d3479 Updating documentation 2024-06-05 16:41:01 +00:00
Documentation Updater
0bd1ace8b5 Updating documentation 2024-06-04 21:28:16 +00:00
Documentation Updater
267aabb361 Updating documentation 2024-05-22 14:51:54 +00:00
Documentation Updater
e83703ee63 Updating documentation 2024-05-17 17:32:34 +00:00
Documentation Updater
22d3fe0c5a Updating documentation 2024-05-01 16:15:18 +00:00
Documentation Updater
b6b719e8b9 Updating documentation 2024-04-29 17:20:10 +00:00
Documentation Updater
ec3ca97994 Updating documentation 2024-04-24 14:04:48 +00:00
Documentation Updater
e37ab06fa3 Updating documentation 2024-04-23 22:24:52 +00:00
Documentation Updater
e522f30140 Updating documentation 2024-04-22 14:17:26 +00:00
Documentation Updater
1b710afb49 Updating documentation 2024-04-20 05:49:37 +00:00
Documentation Updater
ca80ce3806 Updating documentation 2024-04-16 22:43:58 +00:00
Documentation Updater
6ffb8264a2 Updating documentation 2024-04-16 18:08:16 +00:00
Documentation Updater
aaf5f4250b Updating documentation 2024-04-15 21:34:22 +00:00
Documentation Updater
f4a49617f3 Updating documentation 2024-04-12 17:21:38 +00:00
Documentation Updater
c08d412edb Updating documentation 2024-04-11 17:22:13 +00:00
Documentation Updater
6ea5aa0494 Updating documentation 2024-04-11 01:00:34 +00:00
Documentation Updater
61ee38f3f2 Updating documentation 2024-04-04 21:45:29 +00:00
Documentation Updater
09065420ed Updating documentation 2024-04-02 15:37:58 +00:00
Documentation Updater
6a39482216 Updating documentation 2024-04-01 23:02:56 +00:00
Documentation Updater
640a47bafc Updating documentation 2024-04-01 20:31:39 +00:00
Documentation Updater
c02e8928df Updating documentation 2024-03-27 22:52:52 +00:00
Documentation Updater
834fdb5088 Updating documentation 2024-03-26 22:04:17 +00:00
Documentation Updater
4c99d4c328 Updating documentation 2024-03-20 17:53:41 +00:00
Documentation Updater
3f89143650 Updating documentation 2024-03-19 19:05:09 +00:00
Documentation Updater
652cfd16ab Updating documentation 2024-03-13 22:49:46 +00:00
Documentation Updater
63afc00660 Updating documentation 2024-03-08 21:03:06 +00:00
Documentation Updater
3eb49ad7b6 Updating documentation 2024-02-26 16:04:55 +00:00
Documentation Updater
6911de3e7a Updating documentation 2024-02-23 17:42:15 +00:00
Documentation Updater
673c3bff0e Updating documentation 2024-02-21 21:11:28 +00:00
Documentation Updater
afd4b8a87b Updating documentation 2024-02-20 18:09:25 +00:00
Documentation Updater
37e7fcf699 Updating documentation 2024-02-19 01:34:21 +00:00
Documentation Updater
23a95d617b Updating documentation 2024-02-19 00:17:50 +00:00
Documentation Updater
bfeac37ef4 Updating documentation 2024-02-16 18:34:38 +00:00
Documentation Updater
dc2195d8a9 Updating documentation 2024-02-16 00:00:51 +00:00
Documentation Updater
760ca33abc Updating documentation 2024-02-15 23:58:28 +00:00
Documentation Updater
9debf45ed4 Updating documentation 2024-02-15 00:12:01 +00:00
Documentation Updater
92d07a3446 Updating documentation 2024-02-14 22:57:11 +00:00
Documentation Updater
11213d0e50 Updating documentation 2024-02-09 22:16:00 +00:00
Documentation Updater
a69b382190 Updating documentation 2024-02-09 16:46:44 +00:00
Documentation Updater
1f43ed5d8d Updating documentation 2024-02-07 23:58:05 +00:00
Documentation Updater
191477645d Updating documentation 2024-02-07 00:15:16 +00:00
Documentation Updater
b3190b2b8a Updating documentation 2024-02-05 20:46:36 +00:00
Documentation Updater
2818e5f3ef Updating documentation 2024-01-30 21:28:28 +00:00
Documentation Updater
6696213bbd Updating documentation 2024-01-26 15:54:35 +00:00
Documentation Updater
9fe35de833 Updating documentation 2024-01-25 21:46:00 +00:00
Documentation Updater
8f2ad5972d Updating documentation 2024-01-24 22:07:22 +00:00
Documentation Updater
7aec08439a Updating documentation 2024-01-23 22:24:12 +00:00
Documentation Updater
ca326c934f Updating documentation 2024-01-22 18:17:01 +00:00
Documentation Updater
254d5b286a Updating documentation 2024-01-20 17:40:54 +00:00
Documentation Updater
ad2bb0abb1 Updating documentation 2024-01-20 17:40:27 +00:00
Documentation Updater
a36d86d0d7 Updating documentation 2024-01-19 21:33:53 +00:00
Documentation Updater
165141acaf Updating documentation 2024-01-18 17:45:16 +00:00
Documentation Updater
56e0ffbc24 Updating documentation 2024-01-12 19:27:22 +00:00
Documentation Updater
0a41391984 Updating documentation 2024-01-11 17:55:42 +00:00
Documentation Updater
1f2619cbd8 Updating documentation 2024-01-08 23:03:22 +00:00
Documentation Updater
0d0d2daee0 Updating documentation 2024-01-08 16:42:40 +00:00
Documentation Updater
3166b79356 Updating documentation 2024-01-04 21:35:13 +00:00
Documentation Updater
72e1c95ebf Updating documentation 2024-01-02 23:05:41 +00:00
Documentation Updater
aa886b6dfc Updating documentation 2023-12-22 17:32:23 +00:00
Documentation Updater
9cad167f85 Updating documentation 2023-12-19 20:53:10 +00:00
Documentation Updater
b6f78a3549 Updating documentation 2023-12-18 21:27:53 +00:00
Documentation Updater
3b792a4496 Updating documentation 2023-12-15 20:36:44 +00:00
Documentation Updater
03e5a58c75 Updating documentation 2023-12-15 02:00:35 +00:00
Documentation Updater
a80f44b833 Updating documentation 2023-12-13 23:43:21 +00:00
Documentation Updater
37bc1086b5 Updating documentation 2023-12-13 18:16:01 +00:00
Documentation Updater
8ff089ec91 Updating documentation 2023-12-12 21:18:04 +00:00
Documentation Updater
a1da27dea8 Updating documentation 2023-12-12 15:02:47 +00:00
Documentation Updater
ff4f9f77f2 Updating documentation 2023-12-08 17:46:34 +00:00
Documentation Updater
8eaf4b97b9 Updating documentation 2023-12-07 19:20:43 +00:00
Documentation Updater
835a90813a Updating documentation 2023-12-07 17:23:34 +00:00
Documentation Updater
a26bdb0666 Updating documentation 2023-12-06 22:25:53 +00:00
Documentation Updater
1a25b65d0d Updating documentation 2023-12-06 22:02:54 +00:00
Documentation Updater
bc87852873 Updating documentation 2023-12-06 18:16:32 +00:00
Documentation Updater
44af128526 Updating documentation 2023-12-06 17:41:22 +00:00
Documentation Updater
ab8b1a45e3 Updating documentation 2023-12-01 00:49:39 +00:00
Documentation Updater
c0111255d7 Updating documentation 2023-11-29 22:27:29 +00:00
Documentation Updater
335164df8b Updating documentation 2023-11-29 16:08:59 +00:00
Documentation Updater
488e55ab2c Updating documentation 2023-11-28 18:40:39 +00:00
Documentation Updater
d92d8d5e0f Updating documentation 2023-11-28 17:42:54 +00:00
Documentation Updater
5d97bc9d20 Updating documentation 2023-11-27 15:32:51 +00:00
Documentation Updater
8347ca3201 Updating documentation 2023-11-22 16:02:52 +00:00
Documentation Updater
34033e7cec Updating documentation 2023-11-17 00:55:15 +00:00
Documentation Updater
214bc53fae Updating documentation 2023-11-16 18:18:19 +00:00
Documentation Updater
6ce63945c4 Updating documentation 2023-11-16 02:12:09 +00:00
Documentation Updater
31c862b230 Updating documentation 2023-11-15 07:05:42 +00:00
Documentation Updater
64c5638706 Updating documentation 2023-11-13 19:03:27 +00:00
Documentation Updater
53dd49a25b Updating documentation 2023-11-11 01:56:19 +00:00
Documentation Updater
6cb93fc4f9 Updating documentation 2023-11-02 18:05:27 +00:00
Documentation Updater
ff90f7d9f6 Updating documentation 2023-11-01 21:25:16 +00:00
Documentation Updater
3d5f737b53 Updating documentation 2023-11-01 15:58:14 +00:00
Documentation Updater
029634df06 Updating documentation 2023-10-30 21:32:40 +00:00
Documentation Updater
79f47536f1 Updating documentation 2023-10-30 18:37:43 +00:00
Documentation Updater
27a57e0259 Updating documentation 2023-10-27 14:53:36 +00:00
Documentation Updater
fdca7ea9f1 Updating documentation 2023-10-24 20:58:09 +00:00
Documentation Updater
8f2f872113 Updating documentation 2023-10-20 22:09:44 +00:00
Documentation Updater
46d3d5d4d8 Updating documentation 2023-10-20 18:34:59 +00:00
Documentation Updater
5717ea3b5d Updating documentation 2023-10-20 15:43:29 +00:00
Documentation Updater
809a9f99f1 Updating documentation 2023-10-19 18:58:58 +00:00
Documentation Updater
67c780482b Updating documentation 2023-10-19 18:47:33 +00:00
Documentation Updater
df4263a7ae Updating documentation 2023-10-18 16:33:47 +00:00
Documentation Updater
624cf453bc Updating documentation 2023-10-17 16:59:33 +00:00
Documentation Updater
18bd04b502 Updating documentation 2023-10-12 17:24:49 +00:00
Documentation Updater
eaad560404 Updating documentation 2023-10-11 22:51:14 +00:00
Documentation Updater
07daec5e7e Updating documentation 2023-10-05 23:10:44 +00:00
Documentation Updater
24dc9509fe Updating documentation 2023-10-04 15:09:43 +00:00
Documentation Updater
cd18291036 Updating documentation 2023-10-03 20:20:36 +00:00
Documentation Updater
073c172b4c Updating documentation 2023-09-29 16:42:00 +00:00
Documentation Updater
cbc1296898 Updating documentation 2023-09-27 19:55:25 +00:00
Documentation Updater
2828ce0f6c Updating documentation 2023-09-26 16:41:12 +00:00
Documentation Updater
bd5c8cb450 Updating documentation 2023-09-25 20:48:57 +00:00
Documentation Updater
eb05f2cad4 Updating documentation 2023-09-19 17:54:12 +00:00
Documentation Updater
37bdfa2332 Updating documentation 2023-09-14 23:15:14 +00:00
Documentation Updater
49f7d8b712 Updating documentation 2023-09-13 23:32:01 +00:00
Documentation Updater
5ce8a1f59a Updating documentation 2023-09-13 20:37:26 +00:00
Documentation Updater
61c36b7f4d Updating documentation 2023-09-11 21:06:16 +00:00
Documentation Updater
2fb13541b0 Updating documentation 2023-09-07 22:35:33 +00:00
Documentation Updater
4b3c6de61b Updating documentation 2023-09-07 22:07:10 +00:00
Documentation Updater
5fe3f04ba9 Updating documentation 2023-09-07 17:39:58 +00:00
Documentation Updater
d6098f2c9c Updating documentation 2023-09-06 22:01:56 +00:00
Documentation Updater
fa021ff3e5 Updating documentation 2023-09-05 21:29:38 +00:00
Documentation Updater
8f2cd859e1 Updating documentation 2023-09-05 18:26:38 +00:00
Documentation Updater
9df8b4ef19 Updating documentation 2023-08-31 21:22:29 +00:00
Documentation Updater
17c8a79cf7 Updating documentation 2023-08-30 23:14:35 +00:00
Documentation Updater
74211590b5 Updating documentation 2023-08-29 23:23:36 +00:00
Documentation Updater
27f46c8bf8 Updating documentation 2023-08-25 23:02:39 +00:00
Documentation Updater
e109ff7397 Updating documentation 2023-08-24 17:44:31 +00:00
Documentation Updater
daa0ef3352 Updating documentation 2023-08-24 16:58:59 +00:00
Documentation Updater
a10365e30d Updating documentation 2023-08-17 21:37:59 +00:00
Documentation Updater
6f1c94c84f Updating documentation 2023-08-17 16:59:13 +00:00
Documentation Updater
c06286b0f4 Updating documentation 2023-08-15 18:37:36 +00:00
Documentation Updater
00367022a7 Updating documentation 2023-08-10 18:58:20 +00:00
Documentation Updater
1281831d1c Updating documentation 2023-08-09 18:23:29 +00:00
Documentation Updater
d17f5906d2 Updating documentation 2023-08-09 16:45:16 +00:00
Documentation Updater
d04124a42e Updating documentation 2023-08-04 21:52:28 +00:00
Documentation Updater
c149d1e962 Updating documentation 2023-08-04 20:21:16 +00:00
Documentation Updater
9cb5bfcf2a Updating documentation 2023-08-04 17:24:50 +00:00
Documentation Updater
d709cc4ed3 Updating documentation 2023-08-02 21:40:41 +00:00
Documentation Updater
a95fafde1a Updating documentation 2023-07-28 20:09:03 +00:00
Documentation Updater
16fe53a281 Updating documentation 2023-07-26 23:15:16 +00:00
Documentation Updater
3c7632a9dc Updating documentation 2023-07-21 18:15:28 +00:00
Documentation Updater
ab6b6ab09d Updating documentation 2023-07-19 18:50:27 +00:00
Documentation Updater
652e3b1e06 Updating documentation 2023-07-12 20:57:04 +00:00
Documentation Updater
4eb3cc9d97 Updating documentation 2023-07-07 00:55:02 +00:00
Documentation Updater
6e7a06d7be Updating documentation 2023-07-05 18:43:05 +00:00
Documentation Updater
4215ef6aa9 Updating documentation 2023-06-30 18:25:24 +00:00
Documentation Updater
cff83d8124 Updating documentation 2023-06-30 14:20:13 +00:00
Documentation Updater
1467fce54c Updating documentation 2023-06-28 22:32:21 +00:00
Documentation Updater
9be7bacb18 Updating documentation 2023-06-27 03:26:00 +00:00
Documentation Updater
b76b4ffcc4 Updating documentation 2023-06-16 19:32:43 +00:00
Documentation Updater
687e3bcb18 Updating documentation 2023-06-14 22:40:28 +00:00
Documentation Updater
41b02d4e90 Updating documentation 2023-06-09 15:32:43 +00:00
Documentation Updater
19854af8d8 Updating documentation 2023-06-08 17:43:47 +00:00
Documentation Updater
ca7b63743e Updating documentation 2023-06-06 22:31:49 +00:00
Documentation Updater
9d5cdf11ae Updating documentation 2023-06-05 17:13:59 +00:00
Documentation Updater
d4a24f152a Updating documentation 2023-06-02 18:58:43 +00:00
Documentation Updater
e7c24f1f01 Updating documentation 2023-06-01 05:27:23 +00:00
Documentation Updater
c40c1fafc9 Updating documentation 2023-05-31 21:19:52 +00:00
Documentation Updater
431d6d6f59 Updating documentation 2023-05-26 22:52:40 +00:00
Documentation Updater
2e67c13fe3 Updating documentation 2023-05-26 13:58:18 +00:00
Documentation Updater
87bcacdeb4 Updating documentation 2023-05-25 16:25:03 +00:00
Documentation Updater
728757fbdd Updating documentation 2023-05-18 15:09:42 +00:00
Documentation Updater
9404dfb89e Updating documentation 2023-05-17 18:11:55 +00:00
Documentation Updater
075b268805 Updating documentation 2023-05-12 17:12:35 +00:00
Documentation Updater
632941602b Updating documentation 2023-05-09 21:15:08 +00:00
Documentation Updater
4ea1d13d26 Updating documentation 2023-05-09 19:32:24 +00:00
Documentation Updater
d3f68e7d52 Updating documentation 2023-05-05 18:42:54 +00:00
Documentation Updater
5e1ae88b74 Updating documentation 2023-05-04 17:44:02 +00:00
Documentation Updater
a36f20558f Updating documentation 2023-05-03 19:32:42 +00:00
Documentation Updater
926503fa3f Updating documentation 2023-05-01 21:39:07 +00:00
Documentation Updater
5d0e6ff464 Updating documentation 2023-04-21 20:28:41 +00:00
Documentation Updater
c81cbe9550 Updating documentation 2023-04-10 20:40:53 +00:00
Documentation Updater
4e26676290 Updating documentation 2023-03-29 19:49:59 +00:00
Documentation Updater
5159a594c3 Updating documentation 2023-03-22 21:50:15 +00:00
Documentation Updater
d130ec5785 Updating documentation 2023-03-20 20:42:44 +00:00
Documentation Updater
252b9f6f8b Updating documentation 2023-03-17 19:47:30 +00:00
Documentation Updater
50dfd34754 Updating documentation 2023-02-27 22:42:19 +00:00
Documentation Updater
a1a9db6357 Updating documentation 2023-02-27 22:34:32 +00:00
Documentation Updater
a75fa16527 Updating documentation 2023-02-27 22:34:19 +00:00
Documentation Updater
a67e5ef83b Updating documentation 2023-02-24 23:42:09 +00:00
Documentation Updater
f4ba415202 Updating documentation 2023-02-24 21:26:41 +00:00
Documentation Updater
98e7efa9a0 Updating documentation 2023-02-24 20:31:05 +00:00
Sergey Skrobotov
7dd2e1184b initial commit for the documentation branch 2023-02-22 11:57:41 -08:00
1374 changed files with 46169 additions and 201751 deletions

File diff suppressed because it is too large Load Diff

4
.github/FUNDING.yml vendored
View File

@ -1,4 +0,0 @@
# Copyright 2021 Signal Messenger, LLC
# SPDX-License-Identifier: AGPL-3.0-only
custom: https://signal.org/donate/

View File

@ -1,20 +0,0 @@
version: 2
updates:
- package-ecosystem: github-actions
directory: /
schedule:
interval: monthly
groups:
minor-actions-dependencies:
# GitHub Actions: Only group minor and patch updates (we want to carefully review major updates)
update-types: [ minor, patch ]
- package-ecosystem: maven
directory: /
schedule:
interval: monthly
groups:
minor-java-dependencies:
# Java: Only group minor and patch updates (we want to carefully review major updates)
update-types: [ minor, patch ]

0
.github/stale.yml vendored
View File

View File

@ -1,45 +0,0 @@
name: Update Documentation
on:
push:
branches:
- main
jobs:
build:
permissions:
contents: write
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
with:
distribution: 'temurin'
java-version-file: .java-version
cache: 'maven'
- name: Install gRPC documentation tooling
run: |
sudo apt-get update
sudo apt-get install -y protobuf-compiler
pip install sabledocs
- name: Generate OpenAPI documentation
run: ./mvnw compile
- name: Generate gRPC documentation
run: |
protoc -I service/src/main/proto service/src/main/proto/org/signal/chat/*.proto \
-o api-doc/grpc/descriptor.pb --include_source_info
cd api-doc/grpc && sabledocs
- name: Push documentation to gh-pages
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
git config user.email "github@signal.org"
git config user.name "Documentation Updater"
git fetch origin gh-pages
git checkout gh-pages
cp api-doc/target/openapi/signal-server-openapi.yaml .
rm -rf grpc
cp -r api-doc/grpc/sabledocs_output grpc
git add -A signal-server-openapi.yaml grpc
git diff --cached --quiet || git commit -m "Updating documentation"
git push origin gh-pages -q

View File

@ -1,37 +0,0 @@
name: Integration Tests
on:
schedule:
- cron: '30 19 * * MON-FRI'
workflow_dispatch:
env:
# This may seem a little redundant, but copying the configuration to an environment variable makes it easier and safer
# to then write its contents to a file
INTEGRATION_TEST_CONFIG: ${{ vars.INTEGRATION_TEST_CONFIG }}
jobs:
build:
if: ${{ vars.INTEGRATION_TEST_CONFIG != '' }}
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
with:
distribution: 'temurin'
java-version-file: .java-version
cache: 'maven'
- uses: aws-actions/configure-aws-credentials@e7f100cf4c008499ea8adda475de1042d6975c7b # v6.2.0
name: Configure AWS credentials from Test account
with:
role-to-assume: ${{ vars.AWS_ROLE }}
aws-region: ${{ vars.AWS_REGION }}
- name: Write integration test configuration
run: |
mkdir -p integration-tests/src/main/resources
echo "${INTEGRATION_TEST_CONFIG}" > integration-tests/src/main/resources/config.yml
- name: Run and verify integration tests
run: ./mvnw clean compile test-compile failsafe:integration-test failsafe:verify -P aws-sso

View File

@ -1,67 +0,0 @@
name: Service CI
on:
pull_request:
push:
branches-ignore:
- gh-pages
jobs:
build:
runs-on: ubuntu-latest
container: ubuntu:24.04
timeout-minutes: 20
services:
foundationdb0:
# Note: this should generally match the version of the FoundationDB SERVER deployed in production; no need to
# bump it purely to match the CLIENT version
image: foundationdb/foundationdb:7.3.68
options: --name foundationdb0
foundationdb1:
# Note: this should generally match the version of the FoundationDB SERVER deployed in production; no need to
# bump it purely to match the CLIENT version
image: foundationdb/foundationdb:7.3.68
options: --name foundationdb1
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
- name: Set up JDK
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
with:
distribution: 'temurin'
java-version-file: .java-version
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: Install APT packages
# ca-certificates: required for AWS CRT client
run: |
# Add Docker's official GPG key:
apt update
apt install -y ca-certificates curl
install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
chmod a+r /etc/apt/keyrings/docker.asc
# Add Docker repository to apt sources:
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}") stable" | \
tee /etc/apt/sources.list.d/docker.list > /dev/null
# ca-certificates: required for AWS CRT client
apt update && apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin ca-certificates
- name: Configure FoundationDB0 database
run: docker exec foundationdb0 /usr/bin/fdbcli --exec 'configure new single memory'
- name: Configure FoundationDB1 database
run: docker exec foundationdb1 /usr/bin/fdbcli --exec 'configure new single memory'
- name: Download and install FoundationDB client
run: |
./mvnw -e -B -Pexclude-spam-filter clean prepare-package -DskipTests=true
cp service/target/jib-extra/usr/lib/libfdb_c.so /usr/lib/libfdb_c.x86_64.so
ldconfig
- name: Build with Maven
run: ./mvnw -e -B clean verify -DfoundationDb.serviceContainerNamePrefix=foundationdb

35
.gitignore vendored
View File

@ -1,35 +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
.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
.DS_Store
# Generated gRPC documentation build artifacts
/api-doc/grpc/descriptor.pb
/api-doc/grpc/sabledocs_output/

11
.gitmodules vendored
View File

@ -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

View File

@ -1 +0,0 @@
temurin-25

View File

@ -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.9.0</version>
</extension>
</extensions>

View File

@ -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>

Binary file not shown.

View File

@ -1,20 +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.9.14/apache-maven-3.9.14-bin.zip
wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.3.4/maven-wrapper-3.3.4.jar
distributionSha256Sum=55fadd669532a3205d5db95f490bf13971d8b0843526f407f29db0e61f074ab3
wrapperSha256Sum=4e2fbf6554bc8a4702cdfdd3bef464f423393d784ddbb037216320ce55d5e4e1

View File

@ -296,7 +296,7 @@ commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keysManager, or other information required to install
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object

View File

@ -1,47 +0,0 @@
Signal-Server
=================
Documentation
-------------
Looking for protocol documentation? Check out the website!
https://signal.org/docs/
How to Build
------------
This project uses [FoundationDB](https://www.foundationdb.org/) and requires the FoundationDB client library to be installed on the host system. With that in place, the server can be built and tested with:
```shell script
$ ./mvnw clean test
```
Security
--------
Security issues should be sent to <a href=mailto:security@signal.org>security@signal.org</a>.
Help
----
We cannot provide direct technical support. Get help running this software in your own environment in our [unofficial community forum][community forum].
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 Signal Messenger, LLC
Licensed under the GNU AGPLv3: https://www.gnu.org/licenses/agpl-3.0.html
[community forum]: https://community.signalusers.org

View File

@ -1,30 +0,0 @@
# Testing
## Automated tests
The full suite of automated tests can be run using Maven from the project root:
```sh
./mvnw verify
```
## Test server
The service can be run in a feature-limited test mode by running the Maven `integration-test`
goal with the `test-server` profile activated:
```sh
./mvnw integration-test -Ptest-server [-DskipTests=true]
```
This runs [`LocalWhisperServerService`][lwss] with [test configuration][test.yml] and [secrets][test secrets]. External
registration clients are stubbed so that:
- a captcha requirement can be satisfied with `noop.noop.registration.noop`
- any string will be accepted for a phone verification code
[lwss]: service/src/test/java/org/whispersystems/textsecuregcm/LocalWhisperServerService.java
[test.yml]: service/src/test/resources/config/test.yml
[test secrets]: service/src/test/resources/config/test-secrets-bundle.yml

View File

@ -1,2 +0,0 @@
main-page-content-file = "../../service/src/main/proto/org/signal/chat/README.md"
markdown-extensions = ["fenced_code", "tables"]

View File

@ -1,53 +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>api-doc</artifactId>
<dependencies>
<dependency>
<groupId>org.whispersystems.textsecure</groupId>
<artifactId>service</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-maven-plugin-jakarta</artifactId>
<version>${swagger.version}</version>
<configuration>
<outputFileName>signal-server-openapi</outputFileName>
<outputPath>${project.build.directory}/openapi</outputPath>
<outputFormat>YAML</outputFormat>
<configurationFilePath>${project.basedir}/src/main/resources/openapi/openapi-configuration.yaml
</configurationFilePath>
</configuration>
<executions>
<execution>
<phase>compile</phase>
<goals>
<goal>resolve</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>com.google.cloud.tools</groupId>
<artifactId>jib-maven-plugin</artifactId>
<configuration>
<!-- we don't want jib to execute on this module -->
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -1,97 +0,0 @@
/*
* Copyright 2023 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.signal.openapi;
import com.fasterxml.jackson.annotation.JsonView;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.type.SimpleType;
import io.dropwizard.auth.Auth;
import io.swagger.v3.jaxrs2.ResolvedParameter;
import io.swagger.v3.jaxrs2.ext.AbstractOpenAPIExtension;
import io.swagger.v3.jaxrs2.ext.OpenAPIExtension;
import io.swagger.v3.oas.models.Components;
import jakarta.ws.rs.Consumes;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.util.Iterator;
import java.util.List;
import java.util.Optional;
import java.util.ServiceLoader;
import java.util.Set;
import org.whispersystems.textsecuregcm.auth.AuthenticatedDevice;
/**
* One of the extension mechanisms of Swagger Core library (OpenAPI processor) is via custom implementations
* of the {@link AbstractOpenAPIExtension} class.
* <p/>
* The purpose of this extension is to customize certain aspects of the OpenAPI model generation on a lower level.
* This extension works in coordination with {@link OpenApiReader} that has access to the model on a higher level.
* <p/>
* The extension is enabled by being listed in {@code META-INF/services/io.swagger.v3.jaxrs2.ext.OpenAPIExtension} file.
* @see ServiceLoader
* @see OpenApiReader
* @see <a href="https://github.com/swagger-api/swagger-core/wiki/Swagger-2.X---Extensions">Swagger 2.X Extensions</a>
*/
public class OpenApiExtension extends AbstractOpenAPIExtension {
public static final ResolvedParameter AUTHENTICATED_ACCOUNT = new ResolvedParameter();
public static final ResolvedParameter OPTIONAL_AUTHENTICATED_ACCOUNT = new ResolvedParameter();
/**
* When parsing endpoint methods, Swagger will treat the first parameter not annotated as header/path/query param
* as a request body (and will ignore other not annotated parameters). In our case, this behavior conflicts with
* the {@code @Auth}-annotated parameters. Here we're checking if parameters are known to be anything other than
* a body and return an appropriate {@link ResolvedParameter} representation.
*/
@Override
public ResolvedParameter extractParameters(
final List<Annotation> annotations,
final Type type,
final Set<Type> typesToSkip,
final Components components,
final Consumes classConsumes,
final Consumes methodConsumes,
final boolean includeRequestBody,
final JsonView jsonViewAnnotation,
final Iterator<OpenAPIExtension> chain) {
if (annotations.stream().anyMatch(a -> a.annotationType().equals(Auth.class))) {
// this is the case of authenticated endpoint,
if (type instanceof SimpleType simpleType
&& simpleType.getRawClass().equals(AuthenticatedDevice.class)) {
return AUTHENTICATED_ACCOUNT;
}
if (type instanceof SimpleType simpleType
&& isOptionalOfType(simpleType, AuthenticatedDevice.class)) {
return OPTIONAL_AUTHENTICATED_ACCOUNT;
}
}
return super.extractParameters(
annotations,
type,
typesToSkip,
components,
classConsumes,
methodConsumes,
includeRequestBody,
jsonViewAnnotation,
chain);
}
private static boolean isOptionalOfType(final SimpleType simpleType, final Class<?> expectedType) {
if (!simpleType.getRawClass().equals(Optional.class)) {
return false;
}
final List<JavaType> typeParameters = simpleType.getBindings().getTypeParameters();
if (typeParameters.isEmpty()) {
return false;
}
return typeParameters.get(0) instanceof SimpleType optionalParameterType
&& optionalParameterType.getRawClass().equals(expectedType);
}
}

View File

@ -1,71 +0,0 @@
/*
* Copyright 2023 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.signal.openapi;
import static com.google.common.base.MoreObjects.firstNonNull;
import static org.signal.openapi.OpenApiExtension.AUTHENTICATED_ACCOUNT;
import static org.signal.openapi.OpenApiExtension.OPTIONAL_AUTHENTICATED_ACCOUNT;
import com.fasterxml.jackson.annotation.JsonView;
import com.google.common.collect.ImmutableList;
import io.swagger.v3.jaxrs2.Reader;
import io.swagger.v3.jaxrs2.ResolvedParameter;
import io.swagger.v3.oas.models.Operation;
import io.swagger.v3.oas.models.security.SecurityRequirement;
import jakarta.ws.rs.Consumes;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.util.Collections;
import java.util.List;
/**
* One of the extension mechanisms of Swagger Core library (OpenAPI processor) is via custom implementations
* of the {@link Reader} class.
* <p/>
* The purpose of this extension is to customize certain aspects of the OpenAPI model generation on a higher level.
* This extension works in coordination with {@link OpenApiExtension} that has access to the model on a lower level.
* <p/>
* The extension is enabled by being listed in {@code resources/openapi/openapi-configuration.yaml} file.
* @see OpenApiExtension
* @see <a href="https://github.com/swagger-api/swagger-core/wiki/Swagger-2.X---Extensions">Swagger 2.X Extensions</a>
*/
public class OpenApiReader extends Reader {
private static final String AUTHENTICATED_ACCOUNT_AUTH_SCHEMA = "authenticatedAccount";
/**
* Overriding this method allows converting a resolved parameter into other operation entities,
* in this case, into security requirements.
*/
@Override
protected ResolvedParameter getParameters(
final Type type,
final List<Annotation> annotations,
final Operation operation,
final Consumes classConsumes,
final Consumes methodConsumes,
final JsonView jsonViewAnnotation) {
final ResolvedParameter resolved = super.getParameters(
type, annotations, operation, classConsumes, methodConsumes, jsonViewAnnotation);
if (resolved == AUTHENTICATED_ACCOUNT) {
operation.setSecurity(ImmutableList.<SecurityRequirement>builder()
.addAll(firstNonNull(operation.getSecurity(), Collections.emptyList()))
.add(new SecurityRequirement().addList(AUTHENTICATED_ACCOUNT_AUTH_SCHEMA))
.build());
}
if (resolved == OPTIONAL_AUTHENTICATED_ACCOUNT) {
operation.setSecurity(ImmutableList.<SecurityRequirement>builder()
.addAll(firstNonNull(operation.getSecurity(), Collections.emptyList()))
.add(new SecurityRequirement().addList(AUTHENTICATED_ACCOUNT_AUTH_SCHEMA))
.add(new SecurityRequirement())
.build());
}
return resolved;
}
}

View File

@ -1 +0,0 @@
org.signal.openapi.OpenApiExtension

View File

@ -1,25 +0,0 @@
resourcePackages:
- org.whispersystems.textsecuregcm
prettyPrint: true
cacheTTL: 0
readerClass: org.signal.openapi.OpenApiReader
openAPI:
info:
title: Signal Server API
license:
name: AGPL-3.0-only
url: https://www.gnu.org/licenses/agpl-3.0.txt
servers:
- url: https://chat.signal.org
description: Production service
- url: https://chat.staging.signal.org
description: Staging service
components:
securitySchemes:
authenticatedAccount:
type: http
scheme: basic
description: |
Account authentication is based on Basic authentication schema,
where `username` has a format of `<user_id>[.<device_id>]`. If `device_id` is not specified,
user's `main` device is assumed.

BIN
favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

291
grpc/index.html Normal file
View 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>

File diff suppressed because it is too large Load Diff

View 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>

File diff suppressed because it is too large Load Diff

View 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>

View 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>

View 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>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View 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>

View 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>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View 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&lt;string, string&gt;">
<span>map&lt;string, string&gt;</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>

File diff suppressed because it is too large Load Diff

View 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>

File diff suppressed because it is too large Load Diff

View 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

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

3475
grpc/static/lunr.js Normal file

File diff suppressed because it is too large Load Diff

8083
grpc/static/mystyles.css Normal file

File diff suppressed because it is too large Load Diff

54
grpc/static/theme.js Normal file
View 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
View 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>

View File

@ -1,2 +0,0 @@
.libs
src/main/resources/config.yml

View File

@ -1,93 +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>integration-tests</artifactId>
<properties>
<jetty.http2-client.version>12.1.5</jetty.http2-client.version>
</properties>
<dependencies>
<dependency>
<groupId>org.whispersystems.textsecure</groupId>
<artifactId>service</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>dynamodb</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.websocket</groupId>
<artifactId>jetty-websocket-jetty-api</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.websocket</groupId>
<artifactId>jetty-websocket-jetty-client</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.http2</groupId>
<artifactId>jetty-http2-client-transport</artifactId>
<version>${jetty.http2-client.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<excludes>
<exclude>**</exclude>
</excludes>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<configuration>
<additionalClasspathElements>
<additionalClasspathElement>${project.basedir}/.libs/software.amazon.awssdk-sso.jar</additionalClasspathElement>
</additionalClasspathElements>
<includes>
<include>**/*.java</include>
</includes>
</configuration>
</plugin>
<plugin>
<groupId>com.google.cloud.tools</groupId>
<artifactId>jib-maven-plugin</artifactId>
<configuration>
<!-- we don't want jib to execute on this module -->
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>aws-sso</id>
<dependencies>
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>sso</artifactId>
</dependency>
</dependencies>
</profile>
</profiles>
</project>

View File

@ -1,101 +0,0 @@
/*
* Copyright 2023 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.signal.integration;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import java.io.IOException;
import java.util.Base64;
import org.signal.libsignal.protocol.IdentityKey;
import org.signal.libsignal.protocol.ecc.ECPublicKey;
public final class Codecs {
private Codecs() {
// utility class
}
@FunctionalInterface
public interface CheckedFunction<T, R> {
R apply(T t) throws Exception;
}
public static class Base64BasedSerializer<T> extends JsonSerializer<T> {
private final CheckedFunction<T, byte[]> mapper;
public Base64BasedSerializer(final CheckedFunction<T, byte[]> mapper) {
this.mapper = mapper;
}
@Override
public void serialize(final T value, final JsonGenerator gen, final SerializerProvider serializers) throws IOException {
try {
gen.writeString(Base64.getEncoder().withoutPadding().encodeToString(mapper.apply(value)));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
public static class Base64BasedDeserializer<T> extends JsonDeserializer<T> {
private final CheckedFunction<byte[], T> mapper;
public Base64BasedDeserializer(final CheckedFunction<byte[], T> mapper) {
this.mapper = mapper;
}
@Override
public T deserialize(final JsonParser p, final DeserializationContext ctxt) throws IOException {
try {
return mapper.apply(Base64.getDecoder().decode(p.getValueAsString()));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
public static class ByteArraySerializer extends Base64BasedSerializer<byte[]> {
public ByteArraySerializer() {
super(bytes -> bytes);
}
}
public static class ByteArrayDeserializer extends Base64BasedDeserializer<byte[]> {
public ByteArrayDeserializer() {
super(bytes -> bytes);
}
}
public static class ECPublicKeySerializer extends Base64BasedSerializer<ECPublicKey> {
public ECPublicKeySerializer() {
super(ECPublicKey::serialize);
}
}
public static class ECPublicKeyDeserializer extends Base64BasedDeserializer<ECPublicKey> {
public ECPublicKeyDeserializer() {
super(ECPublicKey::new);
}
}
public static class IdentityKeySerializer extends Base64BasedSerializer<IdentityKey> {
public IdentityKeySerializer() {
super(IdentityKey::serialize);
}
}
public static class IdentityKeyDeserializer extends Base64BasedDeserializer<IdentityKey> {
public IdentityKeyDeserializer() {
super(bytes -> new IdentityKey(bytes, 0));
}
}
}

View File

@ -1,90 +0,0 @@
/*
* Copyright 2023 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.signal.integration;
import java.time.Clock;
import java.time.Duration;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.signal.integration.config.Config;
import org.whispersystems.textsecuregcm.metrics.NoopAwsSdkMetricPublisher;
import org.whispersystems.textsecuregcm.registration.VerificationSession;
import org.whispersystems.textsecuregcm.storage.ChangeNumberWaitingPeriods;
import org.whispersystems.textsecuregcm.storage.PhoneNumberIdentifiers;
import org.whispersystems.textsecuregcm.storage.RegistrationRecoveryPasswords;
import org.whispersystems.textsecuregcm.storage.RegistrationRecoveryPasswordsManager;
import org.whispersystems.textsecuregcm.storage.VerificationSessionManager;
import org.whispersystems.textsecuregcm.storage.VerificationSessions;
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider;
import software.amazon.awssdk.services.dynamodb.DynamoDbAsyncClient;
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
public class IntegrationTools {
private final RegistrationRecoveryPasswordsManager registrationRecoveryPasswordsManager;
private final VerificationSessionManager verificationSessionManager;
private final PhoneNumberIdentifiers phoneNumberIdentifiers;
private final ChangeNumberWaitingPeriods changeNumberWaitingPeriods;
public static IntegrationTools create(final Config config) {
final AwsCredentialsProvider credentialsProvider = DefaultCredentialsProvider.builder().build();
final DynamoDbAsyncClient dynamoDbAsyncClient =
config.dynamoDbClient().buildAsyncClient(credentialsProvider, new NoopAwsSdkMetricPublisher());
final DynamoDbClient dynamoDbClient =
config.dynamoDbClient().buildSyncClient(credentialsProvider, new NoopAwsSdkMetricPublisher());
final RegistrationRecoveryPasswords registrationRecoveryPasswords = new RegistrationRecoveryPasswords(
config.dynamoDbTables().registrationRecovery(), Duration.ofDays(1), dynamoDbClient, Clock.systemUTC());
final VerificationSessions verificationSessions = new VerificationSessions(
dynamoDbClient, config.dynamoDbTables().verificationSessions(), Clock.systemUTC());
return new IntegrationTools(
new RegistrationRecoveryPasswordsManager(registrationRecoveryPasswords),
new VerificationSessionManager(verificationSessions),
new PhoneNumberIdentifiers(dynamoDbAsyncClient, config.dynamoDbTables().phoneNumberIdentifiers()),
new ChangeNumberWaitingPeriods(config.dynamoDbTables().changeNumberWaitingPeriods(), dynamoDbClient)
);
}
private IntegrationTools(
final RegistrationRecoveryPasswordsManager registrationRecoveryPasswordsManager,
final VerificationSessionManager verificationSessionManager,
final PhoneNumberIdentifiers phoneNumberIdentifiers,
final ChangeNumberWaitingPeriods changeNumberWaitingPeriods) {
this.registrationRecoveryPasswordsManager = registrationRecoveryPasswordsManager;
this.verificationSessionManager = verificationSessionManager;
this.phoneNumberIdentifiers = phoneNumberIdentifiers;
this.changeNumberWaitingPeriods = changeNumberWaitingPeriods;
}
public void populateRecoveryPassword(final String phoneNumber, final byte[] password) {
try {
final UUID pni = phoneNumberIdentifiers
.getPhoneNumberIdentifier(phoneNumber).get(5, TimeUnit.SECONDS);
registrationRecoveryPasswordsManager.store(pni, password);
} catch (ExecutionException | InterruptedException | TimeoutException e) {
throw new RuntimeException("failed to get pni", e);
}
}
public Optional<String> peekVerificationSessionPushChallenge(final String sessionId) {
return verificationSessionManager.findForId(sessionId).map(VerificationSession::pushChallenge);
}
public void clearChangeNumberWaitingPeriod(TestUser user) {
changeNumberWaitingPeriods.delete(user.aciUuid());
}
}

View File

@ -1,413 +0,0 @@
/*
* Copyright 2023 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.signal.integration;
import static java.util.Objects.requireNonNull;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.google.common.io.Resources;
import com.google.common.net.HttpHeaders;
import io.dropwizard.configuration.ConfigurationValidationException;
import io.dropwizard.jersey.validation.Validators;
import jakarta.validation.ConstraintViolation;
import java.io.IOException;
import java.lang.invoke.MethodHandles;
import java.net.URI;
import java.net.URL;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.util.ArrayList;
import java.util.Base64;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.tuple.Pair;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.http2.client.HTTP2Client;
import org.eclipse.jetty.http2.client.transport.HttpClientTransportOverHTTP2;
import org.eclipse.jetty.io.ClientConnector;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
import org.eclipse.jetty.websocket.client.WebSocketClient;
import org.signal.integration.config.Config;
import org.signal.libsignal.protocol.IdentityKey;
import org.signal.libsignal.protocol.ecc.ECKeyPair;
import org.signal.libsignal.protocol.ecc.ECPublicKey;
import org.signal.libsignal.protocol.kem.KEMKeyPair;
import org.signal.libsignal.protocol.kem.KEMKeyType;
import org.signal.libsignal.protocol.kem.KEMPublicKey;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.whispersystems.textsecuregcm.entities.AccountAttributes;
import org.whispersystems.textsecuregcm.entities.AccountIdentityResponse;
import org.whispersystems.textsecuregcm.entities.DeviceActivationRequest;
import org.whispersystems.textsecuregcm.entities.ECSignedPreKey;
import org.whispersystems.textsecuregcm.entities.KEMSignedPreKey;
import org.whispersystems.textsecuregcm.entities.RegistrationRequest;
import org.whispersystems.textsecuregcm.http.FaultTolerantHttpClient;
import org.whispersystems.textsecuregcm.storage.Device;
import org.whispersystems.textsecuregcm.util.CertificateUtil;
import org.whispersystems.textsecuregcm.util.HeaderUtils;
import org.whispersystems.textsecuregcm.util.HttpUtils;
import org.whispersystems.textsecuregcm.util.SystemMapper;
public final class Operations {
private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
private static final Config CONFIG = loadConfigFromClasspath("config.yml");
private static final IntegrationTools INTEGRATION_TOOLS = IntegrationTools.create(CONFIG);
private static final String USER_AGENT = "integration-test";
private static final FaultTolerantHttpClient CLIENT = buildClient();
private static final WebSocketClient WEB_SOCKET_CLIENT = buildWebSocketClient();
private Operations() {
// utility class
}
public static TestUser newRegisteredUser(final String number) {
final byte[] registrationPassword = populateRandomRecoveryPassword(number);
final String accountPassword = Base64.getEncoder().encodeToString(randomBytes(32));
final TestUser user = TestUser.create(number, accountPassword, registrationPassword);
final AccountAttributes accountAttributes = user.accountAttributes();
final ECKeyPair aciIdentityKeyPair = ECKeyPair.generate();
final ECKeyPair pniIdentityKeyPair = ECKeyPair.generate();
// register account
final RegistrationRequest registrationRequest = new RegistrationRequest(null,
registrationPassword,
accountAttributes,
true,
new IdentityKey(aciIdentityKeyPair.getPublicKey()),
new IdentityKey(pniIdentityKeyPair.getPublicKey()),
new DeviceActivationRequest(generateSignedECPreKey(1, aciIdentityKeyPair),
generateSignedECPreKey(2, pniIdentityKeyPair),
generateSignedKEMPreKey(3, aciIdentityKeyPair),
generateSignedKEMPreKey(4, pniIdentityKeyPair),
Optional.empty(),
Optional.empty()));
final AccountIdentityResponse registrationResponse = apiPost("/v1/registration", registrationRequest)
.authorized(number, accountPassword)
.executeExpectSuccess(AccountIdentityResponse.class);
user.setAciUuid(registrationResponse.uuid());
user.setPniUuid(registrationResponse.pni());
return user;
}
public record PrescribedVerificationNumber(String number, String verificationCode) {}
public static PrescribedVerificationNumber prescribedVerificationNumber() {
return new PrescribedVerificationNumber(
CONFIG.prescribedRegistrationNumber(),
CONFIG.prescribedRegistrationCode());
}
public static void deleteUser(final TestUser user) {
apiDelete("/v1/accounts/me").authorized(user).executeExpectSuccess();
}
public static String peekVerificationSessionPushChallenge(final String sessionId) {
return INTEGRATION_TOOLS.peekVerificationSessionPushChallenge(sessionId)
.orElseThrow(() -> new RuntimeException("push challenge not found for the verification session"));
}
public static byte[] populateRandomRecoveryPassword(final String number) {
final byte[] recoveryPassword = randomBytes(32);
INTEGRATION_TOOLS.populateRecoveryPassword(number, recoveryPassword);
return recoveryPassword;
}
public static void clearChangeNumberWaitingPeriod(final TestUser user) {
INTEGRATION_TOOLS.clearChangeNumberWaitingPeriod(user);
}
public static <T> T sendEmptyRequestAuthenticated(
final String endpoint,
final String method,
final String username,
final String password,
final Class<T> outputType) {
try {
final HttpRequest request = HttpRequest.newBuilder()
.uri(serverUri(endpoint, Collections.emptyList()))
.method(method, HttpRequest.BodyPublishers.noBody())
.header(HttpHeaders.AUTHORIZATION, HeaderUtils.basicAuthHeader(username, password))
.header(HttpHeaders.CONTENT_TYPE, "application/json")
.build();
return CLIENT.sendAsync(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8))
.whenComplete((response, error) -> {
if (error != null) {
logger.error("request error", error);
error.printStackTrace();
} else {
logger.info("response: {}", response.statusCode());
System.out.println("response: " + response.statusCode() + ", " + response.body());
}
})
.thenApply(response -> {
try {
return outputType.equals(Void.class)
? null
: SystemMapper.jsonMapper().readValue(response.body(), outputType);
} catch (final IOException e) {
throw new RuntimeException(e);
}
})
.get();
} catch (final Exception e) {
throw new RuntimeException(e);
}
}
private static byte[] randomBytes(int numBytes) {
final byte[] bytes = new byte[numBytes];
new SecureRandom().nextBytes(bytes);
return bytes;
}
public static RequestBuilder apiGet(final String endpoint) {
return new RequestBuilder(HttpRequest.newBuilder().GET(), endpoint);
}
public static RequestBuilder apiDelete(final String endpoint) {
return new RequestBuilder(HttpRequest.newBuilder().DELETE(), endpoint);
}
public static <R> RequestBuilder apiPost(final String endpoint, final R input) {
return RequestBuilder.withJsonBody(endpoint, "POST", input);
}
public static <R> RequestBuilder apiPut(final String endpoint, final R input) {
return RequestBuilder.withJsonBody(endpoint, "PUT", input);
}
public static <R> RequestBuilder apiPatch(final String endpoint, final R input) {
return RequestBuilder.withJsonBody(endpoint, "PATCH", input);
}
private static URI serverUri(final String endpoint, final List<String> queryParams) {
final String query = queryParams.isEmpty()
? StringUtils.EMPTY
: "?" + String.join("&", queryParams);
return URI.create("https://" + CONFIG.domain() + endpoint + query);
}
public static class RequestBuilder {
private final HttpRequest.Builder builder;
private final String endpoint;
private final List<String> queryParams = new ArrayList<>();
private RequestBuilder(final HttpRequest.Builder builder, final String endpoint) {
this.builder = builder;
this.endpoint = endpoint;
}
private static <R> RequestBuilder withJsonBody(final String endpoint, final String method, final R input) {
final byte[] body = encodeJsonBody(input);
return new RequestBuilder(HttpRequest.newBuilder()
.header(HttpHeaders.CONTENT_TYPE, "application/json")
.method(method, HttpRequest.BodyPublishers.ofByteArray(body)), endpoint);
}
public RequestBuilder authorized(final TestUser user) {
return authorized(user, Device.PRIMARY_ID);
}
public RequestBuilder authorized(final TestUser user, final byte deviceId) {
final String username = "%s.%d".formatted(user.aciUuid().toString(), deviceId);
return authorized(username, user.accountPassword());
}
public RequestBuilder authorized(final String username, final String password) {
builder.header(HttpHeaders.AUTHORIZATION, HeaderUtils.basicAuthHeader(username, password));
return this;
}
public RequestBuilder queryParam(final String key, final String value) {
queryParams.add("%s=%s".formatted(key, value));
return this;
}
public RequestBuilder header(final String name, final String value) {
builder.header(name, value);
return this;
}
public Pair<Integer, Void> execute() {
return execute(Void.class);
}
public Pair<Integer, Void> executeExpectSuccess() {
final Pair<Integer, Void> execute = execute();
Validate.isTrue(
HttpUtils.isSuccessfulResponse(execute.getLeft()),
"Unexpected response code: %d",
execute.getLeft());
return execute;
}
public <T> T executeExpectSuccess(final Class<T> expectedType) {
final Pair<Integer, T> execute = execute(expectedType);
Validate.isTrue(
HttpUtils.isSuccessfulResponse(execute.getLeft()),
"Unexpected response code: %d : %s",
execute.getLeft(), execute.getRight());
return requireNonNull(execute.getRight());
}
public void executeExpectStatusCode(final int expectedStatusCode) {
final Pair<Integer, Void> execute = execute(Void.class);
Validate.isTrue(
execute.getLeft() == expectedStatusCode,
"Unexpected response code: %d",
execute.getLeft()
);
}
public <T> Pair<Integer, T> execute(final Class<T> expectedType) {
builder.uri(serverUri(endpoint, queryParams))
.header(HttpHeaders.USER_AGENT, USER_AGENT);
return CLIENT.sendAsync(builder.build(), HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8))
.whenComplete((response, error) -> {
if (error != null) {
logger.error("request error", error);
error.printStackTrace();
}
})
.thenApply(response -> {
try {
final T result = expectedType.equals(Void.class)
? null
: SystemMapper.jsonMapper().readValue(response.body(), expectedType);
return Pair.of(response.statusCode(), result);
} catch (final IOException e) {
throw new RuntimeException(e);
}
})
.join();
}
}
private static FaultTolerantHttpClient buildClient() {
try {
return FaultTolerantHttpClient.newBuilder("integration-test", Executors.newFixedThreadPool(16))
.withTrustedServerCertificates(CONFIG.rootCert())
.build();
} catch (final CertificateException e) {
throw new RuntimeException(e);
}
}
private static WebSocketClient buildWebSocketClient() {
try {
final KeyStore trustStore = CertificateUtil.buildKeyStoreForPem(CONFIG.rootCert());
final SslContextFactory.Client sslContextFactory = new SslContextFactory.Client();
sslContextFactory.setTrustStore(trustStore);
final ClientConnector connector = new ClientConnector();
connector.setSslContextFactory(sslContextFactory);
final HTTP2Client http2Client = new HTTP2Client(connector);
final HttpClient httpClient = new HttpClient(new HttpClientTransportOverHTTP2(http2Client));
final WebSocketClient wsClient = new WebSocketClient(httpClient);
wsClient.start();
return wsClient;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static WebsocketClientSession authenticatedWebsocket(final TestUser user, final byte deviceId) throws IOException {
final String username = "%s.%d".formatted(user.aciUuid().toString(), deviceId);
return connect("/v1/websocket/", Map.of(HttpHeaders.AUTHORIZATION, HeaderUtils.basicAuthHeader(username, user.accountPassword())));
}
public static WebsocketClientSession anonymousWebsocket() throws IOException {
return connect("/v1/websocket/", Collections.emptyMap());
}
private static WebsocketClientSession connect(
final String path,
final Map<String, String> headers) throws IOException {
final URI uri = URI.create("wss://grpc." + CONFIG.domain() + path);
final ClientUpgradeRequest request = new ClientUpgradeRequest(uri);
headers.forEach(request::setHeader);
final WebsocketClientSession listener = new WebsocketClientSession();
try {
WEB_SOCKET_CLIENT.connect(listener, request).get(5, TimeUnit.SECONDS);
} catch (Exception e) {
throw new IOException(e);
}
logger.info("Successfully connected to websocket on {}", uri);
return listener;
}
private static Config loadConfigFromClasspath(final String filename) {
try {
final URL configFileUrl = Resources.getResource(filename);
final Config config = SystemMapper.yamlMapper().readValue(Resources.toByteArray(configFileUrl), Config.class);
final Set<ConstraintViolation<Config>> constraintViolations = Validators.newValidator().validate(config);
if (!constraintViolations.isEmpty()) {
throw new ConfigurationValidationException(filename, constraintViolations);
}
return config;
} catch (final Exception e) {
throw new RuntimeException(e);
}
}
public static ECSignedPreKey generateSignedECPreKey(final long id, final ECKeyPair identityKeyPair) {
final ECPublicKey pubKey = ECKeyPair.generate().getPublicKey();
final byte[] signature = identityKeyPair.getPrivateKey().calculateSignature(pubKey.serialize());
return new ECSignedPreKey(id, pubKey, signature);
}
public static KEMSignedPreKey generateSignedKEMPreKey(final long id, final ECKeyPair identityKeyPair) {
final KEMPublicKey pubKey = KEMKeyPair.generate(KEMKeyType.KYBER_1024).getPublicKey();
final byte[] signature = identityKeyPair.getPrivateKey().calculateSignature(pubKey.serialize());
return new KEMSignedPreKey(id, pubKey, signature);
}
public static <R> byte[] encodeJsonBody(final R input) {
try {
return SystemMapper.jsonMapper().writeValueAsBytes(input);
} catch (final JsonProcessingException e) {
throw new RuntimeException(e);
}
}
}

View File

@ -1,58 +0,0 @@
/*
* Copyright 2023 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.signal.integration;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.lang3.tuple.Pair;
import org.signal.libsignal.protocol.IdentityKeyPair;
import org.signal.libsignal.protocol.ecc.ECKeyPair;
import org.signal.libsignal.protocol.state.SignedPreKeyRecord;
public class TestDevice {
private final byte deviceId;
private final Map<Integer, Pair<IdentityKeyPair, SignedPreKeyRecord>> signedPreKeys = new ConcurrentHashMap<>();
public static TestDevice create(
final byte deviceId,
final IdentityKeyPair aciIdentityKeyPair,
final IdentityKeyPair pniIdentityKeyPair) {
final TestDevice device = new TestDevice(deviceId);
device.addSignedPreKey(aciIdentityKeyPair);
device.addSignedPreKey(pniIdentityKeyPair);
return device;
}
public TestDevice(final byte deviceId) {
this.deviceId = deviceId;
}
public byte deviceId() {
return deviceId;
}
public SignedPreKeyRecord latestSignedPreKey(final IdentityKeyPair identity) {
final int id = signedPreKeys.entrySet()
.stream()
.filter(p -> p.getValue().getLeft().equals(identity))
.mapToInt(Map.Entry::getKey)
.max()
.orElseThrow();
return signedPreKeys.get(id).getRight();
}
public SignedPreKeyRecord addSignedPreKey(final IdentityKeyPair identity) {
final int nextId = signedPreKeys.keySet().stream().mapToInt(k -> k + 1).max().orElse(0);
final ECKeyPair keyPair = ECKeyPair.generate();
final byte[] signature = keyPair.getPrivateKey().calculateSignature(keyPair.getPublicKey().serialize());
final SignedPreKeyRecord signedPreKeyRecord = new SignedPreKeyRecord(nextId, System.currentTimeMillis(), keyPair, signature);
signedPreKeys.put(nextId, Pair.of(identity, signedPreKeyRecord));
return signedPreKeyRecord;
}
}

View File

@ -1,199 +0,0 @@
/*
* Copyright 2023 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.signal.integration;
import static java.util.Objects.requireNonNull;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import java.security.SecureRandom;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import org.signal.libsignal.protocol.IdentityKey;
import org.signal.libsignal.protocol.IdentityKeyPair;
import org.signal.libsignal.protocol.InvalidKeyException;
import org.signal.libsignal.protocol.ecc.ECPublicKey;
import org.signal.libsignal.protocol.state.SignedPreKeyRecord;
import org.signal.libsignal.protocol.util.KeyHelper;
import org.whispersystems.textsecuregcm.auth.UnidentifiedAccessUtil;
import org.whispersystems.textsecuregcm.entities.AccountAttributes;
import org.whispersystems.textsecuregcm.storage.Device;
import org.whispersystems.textsecuregcm.storage.DeviceCapability;
public class TestUser {
private final int registrationId;
private final int pniRegistrationId;
private final IdentityKeyPair aciIdentityKey;
private final Map<Byte, TestDevice> devices = new ConcurrentHashMap<>();
private final byte[] unidentifiedAccessKey;
private String phoneNumber;
private IdentityKeyPair pniIdentityKey;
private String accountPassword;
private byte[] registrationPassword;
private UUID aciUuid;
private UUID pniUuid;
public static TestUser create(final String phoneNumber, final String accountPassword, final byte[] registrationPassword) {
// ACI identity key pair
final IdentityKeyPair aciIdentityKey = IdentityKeyPair.generate();
// PNI identity key pair
final IdentityKeyPair pniIdentityKey = IdentityKeyPair.generate();
// registration id
final int registrationId = KeyHelper.generateRegistrationId(false);
final int pniRegistrationId = KeyHelper.generateRegistrationId(false);
// uak
final byte[] unidentifiedAccessKey = new byte[UnidentifiedAccessUtil.UNIDENTIFIED_ACCESS_KEY_LENGTH];
new SecureRandom().nextBytes(unidentifiedAccessKey);
return new TestUser(
registrationId,
pniRegistrationId,
aciIdentityKey,
phoneNumber,
pniIdentityKey,
unidentifiedAccessKey,
accountPassword,
registrationPassword);
}
public TestUser(
final int registrationId,
final int pniRegistrationId,
final IdentityKeyPair aciIdentityKey,
final String phoneNumber,
final IdentityKeyPair pniIdentityKey,
final byte[] unidentifiedAccessKey,
final String accountPassword,
final byte[] registrationPassword) {
this.registrationId = registrationId;
this.pniRegistrationId = pniRegistrationId;
this.aciIdentityKey = aciIdentityKey;
this.phoneNumber = phoneNumber;
this.pniIdentityKey = pniIdentityKey;
this.unidentifiedAccessKey = unidentifiedAccessKey;
this.accountPassword = accountPassword;
this.registrationPassword = registrationPassword;
devices.put(Device.PRIMARY_ID, TestDevice.create(Device.PRIMARY_ID, aciIdentityKey, pniIdentityKey));
}
public int registrationId() {
return registrationId;
}
public IdentityKeyPair aciIdentityKey() {
return aciIdentityKey;
}
public String phoneNumber() {
return phoneNumber;
}
public IdentityKeyPair pniIdentityKey() {
return pniIdentityKey;
}
public String accountPassword() {
return accountPassword;
}
public byte[] registrationPassword() {
return registrationPassword;
}
public UUID aciUuid() {
return aciUuid;
}
public UUID pniUuid() {
return pniUuid;
}
public AccountAttributes accountAttributes() {
return new AccountAttributes(true, registrationId, pniRegistrationId, "".getBytes(StandardCharsets.UTF_8), "", true,
DeviceCapability.CAPABILITIES_REQUIRED_FOR_NEW_DEVICES)
.setUnidentifiedAccessKey(unidentifiedAccessKey)
.setRecoveryPassword(registrationPassword);
}
public void setAciUuid(final UUID aciUuid) {
this.aciUuid = aciUuid;
}
public void setPniUuid(final UUID pniUuid) {
this.pniUuid = pniUuid;
}
public void setPhoneNumber(final String phoneNumber) {
this.phoneNumber = phoneNumber;
}
public void setPniIdentityKey(final IdentityKeyPair pniIdentityKey) {
this.pniIdentityKey = pniIdentityKey;
}
public void setAccountPassword(final String accountPassword) {
this.accountPassword = accountPassword;
}
public void setRegistrationPassword(final byte[] registrationPassword) {
this.registrationPassword = registrationPassword;
}
public PreKeySetPublicView preKeys(final byte deviceId, final boolean pni) {
final IdentityKeyPair identity = pni
? pniIdentityKey
: aciIdentityKey;
final TestDevice device = requireNonNull(devices.get(deviceId));
final SignedPreKeyRecord signedPreKeyRecord = device.latestSignedPreKey(identity);
try {
return new PreKeySetPublicView(
Collections.emptyList(),
identity.getPublicKey(),
new SignedPreKeyPublicView(
signedPreKeyRecord.getId(),
signedPreKeyRecord.getKeyPair().getPublicKey(),
signedPreKeyRecord.getSignature()
)
);
} catch (InvalidKeyException e) {
throw new RuntimeException(e);
}
}
public record SignedPreKeyPublicView(
int keyId,
@JsonSerialize(using = Codecs.ECPublicKeySerializer.class)
@JsonDeserialize(using = Codecs.ECPublicKeyDeserializer.class)
ECPublicKey publicKey,
@JsonSerialize(using = Codecs.ByteArraySerializer.class)
@JsonDeserialize(using = Codecs.ByteArrayDeserializer.class)
byte[] signature) {
}
public record PreKeySetPublicView(
List<String> preKeys,
@JsonSerialize(using = Codecs.IdentityKeySerializer.class)
@JsonDeserialize(using = Codecs.IdentityKeyDeserializer.class)
IdentityKey identityKey,
SignedPreKeyPublicView signedPreKey) {
}
}

View File

@ -1,150 +0,0 @@
/*
* Copyright 2026 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.signal.integration;
import com.google.protobuf.InvalidProtocolBufferException;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.ByteBuffer;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicLong;
import org.eclipse.jetty.websocket.api.Callback;
import org.eclipse.jetty.websocket.api.Session;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.whispersystems.textsecuregcm.entities.MessageProtos;
import org.whispersystems.textsecuregcm.util.SystemMapper;
import org.whispersystems.websocket.messages.WebSocketMessage;
import org.whispersystems.websocket.messages.WebSocketMessageFactory;
import org.whispersystems.websocket.messages.WebSocketRequestMessage;
import org.whispersystems.websocket.messages.WebSocketResponseMessage;
import org.whispersystems.websocket.messages.protobuf.ProtobufWebSocketMessageFactory;
public class WebsocketClientSession implements Session.Listener.AutoDemanding {
private static final Logger log = LoggerFactory.getLogger(WebsocketClientSession.class);
private final WebSocketMessageFactory messageFactory = new ProtobufWebSocketMessageFactory();
private final AtomicLong requestId = new AtomicLong();
private final ConcurrentHashMap<Long, CompletableFuture<WebSocketResponseMessage>> responseFutures = new ConcurrentHashMap<>();
private final List<MessageProtos.Envelope> receivedEnvelopes = new CopyOnWriteArrayList<>();
private final CompletableFuture<Session> opened = new CompletableFuture<>();
private final CompletableFuture<Void> queueEmpty = new CompletableFuture<>();
private final CompletableFuture<Integer> closed = new CompletableFuture<>();
@Override
public void onWebSocketOpen(final Session session) {
opened.complete(session);
}
@Override
public void onWebSocketBinary(final ByteBuffer payload, final Callback callback) {
try {
final WebSocketMessage message = messageFactory.parseMessage(payload);
switch (message.getType()) {
case REQUEST_MESSAGE -> {
log.info("received request message {} {}", message.getRequestMessage().getVerb(), message.getRequestMessage().getPath());
switch (message.getRequestMessage().getPath()) {
case "/api/v1/message" -> acknowledge(message.getRequestMessage());
case "/api/v1/queue/empty" -> queueEmpty.complete(null);
default -> throw new IllegalStateException("Unexpected path: " + message.getRequestMessage().getPath());
}
}
case RESPONSE_MESSAGE -> {
final WebSocketResponseMessage response = message.getResponseMessage();
log.info("received response message {}", response.getStatus());
final CompletableFuture<WebSocketResponseMessage> future = responseFutures.remove(response.getRequestId());
if (future == null) {
throw new IllegalArgumentException("Received response with no matching request: " + response.getRequestId());
}
future.complete(response);
}
default -> throw new IllegalStateException("Unexpected message type: " + message.getType());
}
callback.succeed();
} catch (final Exception e) {
log.warn("Failed to process message received over the websocket", e);
callback.fail(e);
opened.join().close(1006, e.getMessage(), Callback.NOOP);
}
}
@Override
public void onWebSocketClose(final int statusCode, final String reason, final Callback callback) {
log.info("Received websocket close: {}", statusCode);
closed.complete(statusCode);
final IOException exception = new IOException("WebSocket closed: " + statusCode + " " + reason);
responseFutures.values()
.forEach(f -> f.completeExceptionally(exception));
responseFutures.clear();
if (!queueEmpty.isDone()) {
queueEmpty.completeExceptionally(exception);
}
callback.succeed();
}
public <T> WebSocketResponseMessage sendRequest(
final String verb,
final String path,
final List<String> headers,
final T body) {
final Session session = opened.join();
final long id = requestId.incrementAndGet();
final CompletableFuture<WebSocketResponseMessage> future = new CompletableFuture<>();
responseFutures.put(id, future);
final Optional<byte[]> maybeBody = Optional.ofNullable(body).map(Operations::encodeJsonBody);
final byte[] bytes = messageFactory.createRequest(Optional.of(id), verb, path, headers, maybeBody).toByteArray();
session.sendBinary(ByteBuffer.wrap(bytes), Callback.from(() -> {}, throwable -> {
if (responseFutures.remove(id) != null) {
future.completeExceptionally(throwable);
}
}));
return future.join();
}
public List<MessageProtos.Envelope> getReceivedEnvelopes() {
return receivedEnvelopes;
}
public void waitForQueueEmpty() {
queueEmpty.join();
}
public void close(final int closeCode) {
final Session session = opened.join();
session.close(closeCode, "client close", Callback.NOOP);
closed.join();
}
private void acknowledge(WebSocketRequestMessage message) {
final byte[] envelopeBytes = message.getBody()
.orElseThrow(() -> new IllegalStateException("Messages should have a response body"));
try {
final MessageProtos.Envelope envelope = MessageProtos.Envelope.parseFrom(envelopeBytes);
receivedEnvelopes.add(envelope);
final Session session = opened.join();
final WebSocketMessage response = messageFactory.createResponse(message.getRequestId(), 200, "",
Collections.emptyList(), Optional.empty());
session.sendBinary(ByteBuffer.wrap(response.toByteArray()), Callback.NOOP);
} catch (InvalidProtocolBufferException e) {
throw new IllegalStateException(e);
}
}
public static <R> R decode(Class<R> expectedType, WebSocketResponseMessage message) {
try {
return SystemMapper.jsonMapper()
.readValue(message.getBody().orElseThrow(() -> new IllegalStateException("No response body")), expectedType);
} catch (final IOException e) {
throw new UncheckedIOException(e);
}
}
}

View File

@ -1,19 +0,0 @@
/*
* Copyright 2023 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.signal.integration.config;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import org.whispersystems.textsecuregcm.configuration.DynamoDbClientFactory;
public record Config(@NotBlank String domain,
@NotBlank String rootCert,
@NotNull @Valid DynamoDbClientFactory dynamoDbClient,
@NotNull @Valid DynamoDbTables dynamoDbTables,
@NotBlank String prescribedRegistrationNumber,
@NotBlank String prescribedRegistrationCode) {
}

View File

@ -1,14 +0,0 @@
/*
* Copyright 2023 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.signal.integration.config;
import jakarta.validation.constraints.NotBlank;
public record DynamoDbTables(@NotBlank String registrationRecovery,
@NotBlank String verificationSessions,
@NotBlank String phoneNumberIdentifiers,
@NotBlank String changeNumberWaitingPeriods) {
}

View File

@ -1,150 +0,0 @@
/*
* Copyright 2023 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.signal.integration;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import java.util.Arrays;
import java.util.Base64;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.http.HttpStatus;
import org.junit.jupiter.api.Test;
import org.signal.libsignal.protocol.IdentityKey;
import org.signal.libsignal.protocol.ecc.ECKeyPair;
import org.signal.libsignal.usernames.BaseUsernameException;
import org.signal.libsignal.usernames.Username;
import org.whispersystems.textsecuregcm.entities.AccountIdentifierResponse;
import org.whispersystems.textsecuregcm.entities.AccountIdentityResponse;
import org.whispersystems.textsecuregcm.entities.ChangeNumberRequest;
import org.whispersystems.textsecuregcm.entities.ConfirmUsernameHashRequest;
import org.whispersystems.textsecuregcm.entities.ReserveUsernameHashRequest;
import org.whispersystems.textsecuregcm.entities.ReserveUsernameHashResponse;
import org.whispersystems.textsecuregcm.entities.UsernameHashResponse;
import org.whispersystems.textsecuregcm.identity.AciServiceIdentifier;
import org.whispersystems.textsecuregcm.storage.Device;
public class AccountTest {
@Test
public void testCreateAccount() {
final TestUser user = Operations.newRegisteredUser("+19995550101");
try {
final Pair<Integer, AccountIdentityResponse> execute = Operations.apiGet("/v1/accounts/whoami")
.authorized(user)
.execute(AccountIdentityResponse.class);
assertEquals(HttpStatus.SC_OK, execute.getLeft());
} finally {
Operations.deleteUser(user);
}
}
@Test
public void changePhoneNumber() {
final TestUser user = Operations.newRegisteredUser("+19995550301");
final String targetNumber = "+19995550302";
final ECKeyPair pniIdentityKeyPair = ECKeyPair.generate();
final ChangeNumberRequest changeNumberRequest = new ChangeNumberRequest(null,
Operations.populateRandomRecoveryPassword(targetNumber),
targetNumber,
null,
new IdentityKey(pniIdentityKeyPair.getPublicKey()),
Collections.emptyList(),
Map.of(Device.PRIMARY_ID, Operations.generateSignedECPreKey(1, pniIdentityKeyPair)),
Map.of(Device.PRIMARY_ID, Operations.generateSignedKEMPreKey(2, pniIdentityKeyPair)),
Map.of(Device.PRIMARY_ID, 17));
try {
Operations.clearChangeNumberWaitingPeriod(user);
final AccountIdentityResponse accountIdentityResponse =
Operations.apiPut("/v2/accounts/number", changeNumberRequest)
.authorized(user)
.executeExpectSuccess(AccountIdentityResponse.class);
assertEquals(user.aciUuid(), accountIdentityResponse.uuid());
assertNotEquals(user.pniUuid(), accountIdentityResponse.pni());
assertEquals(targetNumber, accountIdentityResponse.number());
} finally {
Operations.deleteUser(user);
}
}
@Test
public void testUsernameOperations() throws Exception {
final TestUser user = Operations.newRegisteredUser("+19995550102");
try {
verifyFullUsernameLifecycle(user);
// no do it again to check changing usernames
verifyFullUsernameLifecycle(user);
} finally {
Operations.deleteUser(user);
}
}
private static void verifyFullUsernameLifecycle(final TestUser user) throws BaseUsernameException {
final String preferred = "test";
final List<Username> candidates = Username.candidatesFrom(preferred, preferred.length(), preferred.length() + 1);
// reserve a username
final ReserveUsernameHashRequest reserveUsernameHashRequest = new ReserveUsernameHashRequest(
candidates.stream().map(Username::getHash).toList());
// try unauthorized
Operations
.apiPut("/v1/accounts/username_hash/reserve", reserveUsernameHashRequest)
.executeExpectStatusCode(HttpStatus.SC_UNAUTHORIZED);
final ReserveUsernameHashResponse reserveUsernameHashResponse = Operations
.apiPut("/v1/accounts/username_hash/reserve", reserveUsernameHashRequest)
.authorized(user)
.executeExpectSuccess(ReserveUsernameHashResponse.class);
// find which one is the reserved username
final byte[] reservedHash = reserveUsernameHashResponse.usernameHash();
final Username reservedUsername = candidates.stream()
.filter(u -> Arrays.equals(u.getHash(), reservedHash))
.findAny()
.orElseThrow();
// confirm a username
final ConfirmUsernameHashRequest confirmUsernameHashRequest = new ConfirmUsernameHashRequest(
reservedUsername.getHash(),
reservedUsername.generateProof(),
"cluck cluck i'm a parrot".getBytes()
);
// try unauthorized
Operations
.apiPut("/v1/accounts/username_hash/confirm", confirmUsernameHashRequest)
.executeExpectStatusCode(HttpStatus.SC_UNAUTHORIZED);
Operations
.apiPut("/v1/accounts/username_hash/confirm", confirmUsernameHashRequest)
.authorized(user)
.executeExpectSuccess(UsernameHashResponse.class);
// lookup username
final AccountIdentifierResponse accountIdentifierResponse = Operations
.apiGet("/v1/accounts/username_hash/" + Base64.getUrlEncoder().encodeToString(reservedHash))
.executeExpectSuccess(AccountIdentifierResponse.class);
assertEquals(new AciServiceIdentifier(user.aciUuid()), accountIdentifierResponse.uuid());
// try authorized
Operations
.apiGet("/v1/accounts/username_hash/" + Base64.getUrlEncoder().encodeToString(reservedHash))
.authorized(user)
.executeExpectStatusCode(HttpStatus.SC_BAD_REQUEST);
// delete username
Operations
.apiDelete("/v1/accounts/username_hash")
.authorized(user)
.executeExpectSuccess();
}
}

View File

@ -1,77 +0,0 @@
/*
* Copyright 2023 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.signal.integration;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTimeoutPreemptively;
import com.google.common.net.HttpHeaders;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import jakarta.ws.rs.core.MediaType;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.Timeout;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.whispersystems.textsecuregcm.entities.IncomingMessage;
import org.whispersystems.textsecuregcm.entities.IncomingMessageList;
import org.whispersystems.textsecuregcm.entities.MessageProtos;
import org.whispersystems.textsecuregcm.entities.SendMessageResponse;
import org.whispersystems.textsecuregcm.storage.Device;
import org.whispersystems.websocket.messages.WebSocketResponseMessage;
@Timeout(value = 1, unit = TimeUnit.MINUTES, threadMode = Timeout.ThreadMode.SEPARATE_THREAD)
public class MessagingTest {
TestUser userA;
TestUser userB;
@BeforeEach
public void setup() {
userA = Operations.newRegisteredUser("+19995550102");
userB = Operations.newRegisteredUser("+19995550103");
}
@AfterEach
public void teardown() {
Operations.deleteUser(userA);
Operations.deleteUser(userB);
}
@Test
public void testSendMessageUnsealed() throws IOException {
final byte[] expectedContent = "Hello, World!".getBytes(StandardCharsets.UTF_8);
final IncomingMessage message = new IncomingMessage(1, Device.PRIMARY_ID, userB.registrationId(), expectedContent);
final IncomingMessageList messages = new IncomingMessageList(List.of(message), false, true, System.currentTimeMillis());
final WebsocketClientSession websocketA = Operations.authenticatedWebsocket(userA, Device.PRIMARY_ID);
final WebSocketResponseMessage responseMessage = websocketA.sendRequest(
"PUT",
"/v1/messages/%s".formatted(userB.aciUuid().toString()),
List.of(HttpHeaders.CONTENT_TYPE + ":" + MediaType.APPLICATION_JSON),
messages);
assertEquals(200, responseMessage.getStatus());
assertDoesNotThrow(() -> WebsocketClientSession.decode(SendMessageResponse.class, responseMessage));
final WebsocketClientSession websocketB = Operations.authenticatedWebsocket(userB, Device.PRIMARY_ID);
assertTimeoutPreemptively(Duration.ofSeconds(5), websocketB::waitForQueueEmpty);
assertEquals(1, websocketB.getReceivedEnvelopes().size());
final MessageProtos.Envelope envelope = websocketB.getReceivedEnvelopes().getFirst();
assertArrayEquals(expectedContent, envelope.getContent().toByteArray());
websocketB.close(1000);
websocketA.close(1000);
}
}

View File

@ -1,61 +0,0 @@
/*
* Copyright 2023 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.signal.integration;
import io.micrometer.common.util.StringUtils;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.whispersystems.textsecuregcm.entities.CreateVerificationSessionRequest;
import org.whispersystems.textsecuregcm.entities.SubmitVerificationCodeRequest;
import org.whispersystems.textsecuregcm.entities.UpdateVerificationSessionRequest;
import org.whispersystems.textsecuregcm.entities.VerificationCodeRequest;
import org.whispersystems.textsecuregcm.entities.VerificationSessionResponse;
public class RegistrationTest {
@Test
public void testRegistration() throws Exception {
final UpdateVerificationSessionRequest originalRequest = new UpdateVerificationSessionRequest(
"test", UpdateVerificationSessionRequest.PushTokenType.FCM, null, null, null, null);
final Operations.PrescribedVerificationNumber params = Operations.prescribedVerificationNumber();
final CreateVerificationSessionRequest input = new CreateVerificationSessionRequest(params.number(),
originalRequest);
final VerificationSessionResponse verificationSessionResponse = Operations
.apiPost("/v1/verification/session", input)
.executeExpectSuccess(VerificationSessionResponse.class);
final String sessionId = verificationSessionResponse.id();
Assertions.assertTrue(StringUtils.isNotBlank(sessionId));
final String pushChallenge = Operations.peekVerificationSessionPushChallenge(sessionId);
// supply push challenge
final UpdateVerificationSessionRequest updatedRequest = new UpdateVerificationSessionRequest(
"test", UpdateVerificationSessionRequest.PushTokenType.FCM, pushChallenge, null, null, null);
final VerificationSessionResponse pushChallengeSupplied = Operations
.apiPatch("/v1/verification/session/%s".formatted(sessionId), updatedRequest)
.executeExpectSuccess(VerificationSessionResponse.class);
Assertions.assertTrue(pushChallengeSupplied.allowedToRequestCode());
// request code
final VerificationCodeRequest verificationCodeRequest = new VerificationCodeRequest(
VerificationCodeRequest.Transport.SMS, "android-ng");
final VerificationSessionResponse codeRequested = Operations
.apiPost("/v1/verification/session/%s/code".formatted(sessionId), verificationCodeRequest)
.executeExpectSuccess(VerificationSessionResponse.class);
// verify code
final SubmitVerificationCodeRequest submitVerificationCodeRequest = new SubmitVerificationCodeRequest(
params.verificationCode());
final VerificationSessionResponse codeVerified = Operations
.apiPut("/v1/verification/session/%s/code".formatted(sessionId), submitVerificationCodeRequest)
.executeExpectSuccess(VerificationSessionResponse.class);
}
}

308
mvnw vendored
View File

@ -1,308 +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.
# ----------------------------------------------------------------------------
# ----------------------------------------------------------------------------
# Apache Maven Wrapper startup batch script, version 3.2.0
#
# Required ENV vars:
# ------------------
# JAVA_HOME - location of a JDK home dir
#
# Optional ENV vars
# -----------------
# 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
JAVA_HOME="$(/usr/libexec/java_home)"; export JAVA_HOME
else
JAVA_HOME="/Library/Java/Home"; export JAVA_HOME
fi
fi
;;
esac
if [ -z "$JAVA_HOME" ] ; then
if [ -r /etc/gentoo-release ] ; then
JAVA_HOME=$(java-config --jre-home)
fi
fi
# For Cygwin, ensure paths are in UNIX format before anything is touched
if $cygwin ; then
[ -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 "$JAVA_HOME" ] && [ -d "$JAVA_HOME" ] &&
JAVA_HOME="$(cd "$JAVA_HOME" || (echo "cannot cd into $JAVA_HOME."; exit 1); 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 2>/dev/null; \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
# 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/.." || exit 1; pwd)
fi
# end of workaround
done
printf '%s' "$(cd "$basedir" || exit 1; pwd)"
}
# concatenates all lines of a file
concat_lines() {
if [ -f "$1" ]; then
# Remove \r in case we run on Windows within Git Bash
# and check out the repository with auto CRLF management
# enabled. Otherwise, we may read lines that are delimited with
# \r\n and produce $'-Xarg\r' rather than -Xarg due to word
# splitting rules.
tr -s '\r\n' ' ' < "$1"
fi
}
log() {
if [ "$MVNW_VERBOSE" = true ]; then
printf '%s\n' "$1"
fi
}
BASE_DIR=$(find_maven_basedir "$(dirname "$0")")
if [ -z "$BASE_DIR" ]; then
exit 1;
fi
MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}; export MAVEN_PROJECTBASEDIR
log "$MAVEN_PROJECTBASEDIR"
##########################################################################################
# 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.
##########################################################################################
wrapperJarPath="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar"
if [ -r "$wrapperJarPath" ]; then
log "Found $wrapperJarPath"
else
log "Couldn't find $wrapperJarPath, downloading it ..."
if [ -n "$MVNW_REPOURL" ]; then
wrapperUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
else
wrapperUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
fi
while IFS="=" read -r key value; do
# Remove '\r' from value to allow usage on windows as IFS does not consider '\r' as a separator ( considers space, tab, new line ('\n'), and custom '=' )
safeValue=$(echo "$value" | tr -d '\r')
case "$key" in (wrapperUrl) wrapperUrl="$safeValue"; break ;;
esac
done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties"
log "Downloading from: $wrapperUrl"
if $cygwin; then
wrapperJarPath=$(cygpath --path --windows "$wrapperJarPath")
fi
if command -v wget > /dev/null; then
log "Found wget ... using wget"
[ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--quiet"
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
wget $QUIET "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
else
wget $QUIET --http-user="$MVNW_USERNAME" --http-password="$MVNW_PASSWORD" "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
fi
elif command -v curl > /dev/null; then
log "Found curl ... using curl"
[ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--silent"
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
curl $QUIET -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath"
else
curl $QUIET --user "$MVNW_USERNAME:$MVNW_PASSWORD" -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath"
fi
else
log "Falling back to using Java to download"
javaSource="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.java"
javaClass="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.class"
# For Cygwin, switch paths to Windows format before running javac
if $cygwin; then
javaSource=$(cygpath --path --windows "$javaSource")
javaClass=$(cygpath --path --windows "$javaClass")
fi
if [ -e "$javaSource" ]; then
if [ ! -e "$javaClass" ]; then
log " - Compiling MavenWrapperDownloader.java ..."
("$JAVA_HOME/bin/javac" "$javaSource")
fi
if [ -e "$javaClass" ]; then
log " - Running MavenWrapperDownloader.java ..."
("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$wrapperUrl" "$wrapperJarPath") || rm -f "$wrapperJarPath"
fi
fi
fi
fi
##########################################################################################
# End of extension
##########################################################################################
# If specified, validate the SHA-256 sum of the Maven wrapper jar file
wrapperSha256Sum=""
while IFS="=" read -r key value; do
case "$key" in (wrapperSha256Sum) wrapperSha256Sum=$value; break ;;
esac
done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties"
if [ -n "$wrapperSha256Sum" ]; then
wrapperSha256Result=false
if command -v sha256sum > /dev/null; then
if echo "$wrapperSha256Sum $wrapperJarPath" | sha256sum -c - > /dev/null 2>&1; then
wrapperSha256Result=true
fi
elif command -v shasum > /dev/null; then
if echo "$wrapperSha256Sum $wrapperJarPath" | shasum -a 256 -c > /dev/null 2>&1; then
wrapperSha256Result=true
fi
else
echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available."
echo "Please install either command, or disable validation by removing 'wrapperSha256Sum' from your maven-wrapper.properties."
exit 1
fi
if [ $wrapperSha256Result = false ]; then
echo "Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised." >&2
echo "Investigate or delete $wrapperJarPath to attempt a clean download." >&2
echo "If you updated your Maven version, you need to update the specified wrapperSha256Sum property." >&2
exit 1
fi
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 "$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
# shellcheck disable=SC2086 # safe args
exec "$JAVACMD" \
$MAVEN_OPTS \
$MAVEN_DEBUG_OPTS \
-classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
"-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"

205
mvnw.cmd vendored
View File

@ -1,205 +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 Apache Maven Wrapper startup batch script, version 3.2.0
@REM
@REM Required ENV vars:
@REM JAVA_HOME - location of a JDK home dir
@REM
@REM Optional ENV vars
@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 WRAPPER_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
IF "%%A"=="wrapperUrl" SET WRAPPER_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 WRAPPER_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
)
if "%MVNW_VERBOSE%" == "true" (
echo Couldn't find %WRAPPER_JAR%, downloading it ...
echo Downloading from: %WRAPPER_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('%WRAPPER_URL%', '%WRAPPER_JAR%')"^
"}"
if "%MVNW_VERBOSE%" == "true" (
echo Finished downloading %WRAPPER_JAR%
)
)
@REM End of extension
@REM If specified, validate the SHA-256 sum of the Maven wrapper jar file
SET WRAPPER_SHA_256_SUM=""
FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
IF "%%A"=="wrapperSha256Sum" SET WRAPPER_SHA_256_SUM=%%B
)
IF NOT %WRAPPER_SHA_256_SUM%=="" (
powershell -Command "&{"^
"$hash = (Get-FileHash \"%WRAPPER_JAR%\" -Algorithm SHA256).Hash.ToLower();"^
"If('%WRAPPER_SHA_256_SUM%' -ne $hash){"^
" Write-Output 'Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised.';"^
" Write-Output 'Investigate or delete %WRAPPER_JAR% to attempt a clean download.';"^
" Write-Output 'If you updated your Maven version, you need to update the specified wrapperSha256Sum property.';"^
" exit 1;"^
"}"^
"}"
if ERRORLEVEL 1 goto error
)
@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%

583
pom.xml
View File

@ -1,583 +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>signal-build-artifacts</id>
<name>Signal Build Artifacts</name>
<url>https://build-artifacts.signal.org/libraries/maven</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>api-doc</module>
<module>integration-tests</module>
<module>service</module>
<module>websocket-resources</module>
</modules>
<properties>
<aws.sdk2.version>2.42.27</aws.sdk2.version>
<braintree.version>3.48.0</braintree.version>
<commons-csv.version>1.14.1</commons-csv.version>
<commons-io.version>2.21.0</commons-io.version>
<dropwizard.version>5.0.1</dropwizard.version>
<!-- Note: We use x86_64 builds without AVX instructions enabled (i.e. FoundationDB versions with even-numbered
patch versions). Also when updating FoundationDB, make sure to update the version of FoundationDBused by GitHub
Actions. -->
<foundationdb.version>7.3.68</foundationdb.version>
<foundationdb.api-version>730</foundationdb.api-version>
<foundationdb.client-library-sha256>8c96a1f7ab561cd38e16e4c269c5e50ae0fd8063854e0d72c89339a7ac1b6873</foundationdb.client-library-sha256>
<google-cloud-libraries.version>26.79.0</google-cloud-libraries.version>
<grpc.version>1.76.3</grpc.version> <!-- should be kept in sync with the value from Google libraries-bom -->
<gson.version>2.13.2</gson.version>
<guava.version>33.5.0-jre</guava.version>
<!-- several libraries (AWS, Google Cloud) use Apache http components transitively, and we need to align them -->
<httpcore.version>4.4.16</httpcore.version>
<httpclient.version>4.5.14</httpclient.version>
<jackson.version>2.21.2</jackson.version>
<junit-pioneer.version>2.3.0</junit-pioneer.version>
<jsr305.version>3.0.2</jsr305.version>
<kotlin.version>2.3.20</kotlin.version>
<logback.version>1.5.32</logback.version>
<logback-access-common.version>2.0.12</logback-access-common.version>
<lettuce.version>7.5.1.RELEASE</lettuce.version>
<libphonenumber.version>9.0.21</libphonenumber.version>
<logstash.logback.version>8.1</logstash.logback.version>
<log4j-bom.version>2.25.4</log4j-bom.version>
<luajava.version>3.5.0</luajava.version>
<micrometer.version>1.16.4</micrometer.version>
<netty.version>4.2.13.Final</netty.version>
<!-- Must be less than or equal to the value from Google libraries-bom which controls the protobuf runtime version.
See https://protobuf.dev/support/cross-version-runtime-guarantee/. -->
<protoc.version>4.33.2</protoc.version>
<pushy.version>0.15.4</pushy.version>
<reactor-bom.version>2025.0.4</reactor-bom.version> <!-- 3.8.4, see https://github.com/reactor/reactor#bom-versioning-scheme -->
<resilience4j.version>2.4.0</resilience4j.version>
<semver4j.version>3.1.0</semver4j.version>
<simple-grpc.version>0.2.0</simple-grpc.version>
<slf4j.version>2.0.17</slf4j.version>
<stripe.version>31.2.0</stripe.version>
<swagger.version>2.2.46</swagger.version>
<testcontainers.version>2.0.4</testcontainers.version>
<!-- images to use in tests via testcontainers -->
<dynamodb.image>amazon/dynamodb-local:3.3.0@sha256:d89f8fcc6b1a39cb35976c248ed42a28c66ae00dc043099210f5571e42648ab4</dynamodb.image>
<localstack.image>localstack/localstack:4.13@sha256:46302bcb91a7e8008e6394be8afafdbfa40fb77a54d4046a38be35992042d5de</localstack.image>
<redis.image>redis:7.4-alpine@sha256:af1d0fc3f63b02b13ff7906c9baf7c5b390b8881ca08119cd570677fe2f60b55</redis.image>
<redis-cluster.image>docker.io/bitnamilegacy/redis-cluster:7.4.3@sha256:a53d023fdfaf8a8d7ddc58da040d3494e4cb45772644618ffa44c42dcd32b9af</redis-cluster.image>
<!-- eclipse-temurin:25.0.2_10-jre-noble (note: always use the multi-arch manifest *LIST* here) -->
<docker.image.sha256>d2975fcad8dc9ac2180c0c278c8ae7b3443d36cb80845cb0811c20d9463f5fee</docker.image.sha256>
<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>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.opentelemetry</groupId>
<artifactId>opentelemetry-bom</artifactId>
<version>1.60.1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>io.opentelemetry.instrumentation</groupId>
<artifactId>opentelemetry-instrumentation-bom</artifactId>
<version>2.26.1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-bom</artifactId>
<version>${reactor-bom.version}</version>
<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.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>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.foundationdb</groupId>
<artifactId>fdb-java</artifactId>
<version>${foundationdb.version}</version>
</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>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.3.6</version>
</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.findbugs</groupId>
<artifactId>jsr305</artifactId>
<version>${jsr305.version}</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>${gson.version}</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>${guava.version}</version>
</dependency>
<dependency>
<groupId>com.redis</groupId>
<artifactId>testcontainers-redis</artifactId>
<version>2.2.4</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.signal</groupId>
<artifactId>libsignal-server</artifactId>
<version>0.96.2</version>
</dependency>
<dependency>
<groupId>org.signal</groupId>
<artifactId>simple-grpc-runtime</artifactId>
<version>${simple-grpc.version}</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-bom</artifactId>
<version>${log4j-bom.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpcore</artifactId>
<version>${httpcore.version}</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>${httpclient.version}</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>${logback.version}</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>${logback.version}</version>
</dependency>
<dependency>
<groupId>ch.qos.logback.access</groupId>
<artifactId>logback-access-common</artifactId>
<version>${logback-access-common.version}</version>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers-bom</artifactId>
<version>${testcontainers.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>earth.adi</groupId>
<artifactId>testcontainers-foundationdb</artifactId>
<version>1.1.0</version>
<scope>test</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-all</artifactId>
<version>1.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>aws-crt-client</artifactId>
</dependency>
<dependency>
<groupId>org.wiremock</groupId>
<artifactId>wiremock-jetty12</artifactId>
<version>3.13.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<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>${junit-pioneer.version}</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.1</version>
</extension>
</extensions>
<pluginManagement>
<plugins>
<plugin>
<groupId>com.google.cloud.tools</groupId>
<artifactId>jib-maven-plugin</artifactId>
<version>3.4.6</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.5.5</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<version>3.5.4</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.5.0</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.6.1</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.7.1</version>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>3.6.3</version>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>properties-maven-plugin</artifactId>
<version>1.2.1</version>
</plugin>
</plugins>
</pluginManagement>
<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:${protoc.version}:exe:${os.detected.classifier}</protocArtifact>
<pluginId>grpc-java</pluginId>
<pluginArtifact>io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier}</pluginArtifact>
<protocPlugins>
<protocPlugin>
<id>simple</id>
<groupId>org.signal</groupId>
<artifactId>simple-grpc-generator</artifactId>
<version>${simple-grpc.version}</version>
<mainClass>org.signal.grpc.simple.SimpleGrpcGenerator</mainClass>
</protocPlugin>
</protocPlugins>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>compile-custom</goal>
<goal>test-compile</goal>
<goal>test-compile-custom</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.15.0</version>
<configuration>
<release>25</release>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.5.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.10.0</version>
<executions>
<execution>
<!--
Set dependencies as properties for use in argLine property for mockito jar.
The property isn't needed until the test phase, and deferring it from the default
`initialize` addresses issues running lifecycle phases that precede `test` in isolation.
-->
<phase>process-test-classes</phase>
<goals>
<goal>properties</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
<version>3.6.2</version>
<executions>
<execution>
<goals>
<goal>enforce</goal>
</goals>
<configuration>
<rules>
<dependencyConvergence/>
<requireMavenVersion>
<version>3.9.11</version>
</requireMavenVersion>
</rules>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-install-plugin</artifactId>
<version>3.1.4</version>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<version>3.1.4</version>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -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>

View File

@ -1,104 +0,0 @@
stripe.apiKey: unset
stripe.idempotencyKeyGenerator: abcdefg12345678= # base64 for creating request idempotency hash
braintree.publicKey: unset
braintree.privateKey: unset
appleAppStore.encodedKey: unset
directoryV2.client.userAuthenticationTokenSharedSecret: abcdefghijklmnopqrstuvwxyz0123456789ABCDEFG= # base64-encoded secret shared with CDS to generate auth tokens for Signal users
directoryV2.client.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
svr2.userIdTokenSharedSecret: bbcdefghijklmnopqrstuvwxyz0123456789ABCDEFG= # base64-encoded secret shared with SVR2 to generate auth identity tokens for Signal users
svrb.userAuthenticationTokenSharedSecret: abcdefghijklmnopqrstuvwxyz0123456789ABCDEFG= # base64-encoded secret shared with SVRB to generate auth tokens for Signal users
svrb.userIdTokenSharedSecret: bbcdefghijklmnopqrstuvwxyz0123456789ABCDEFG= # base64-encoded secret shared with SVRB to generate auth identity tokens for Signal users
tus.userAuthenticationTokenSharedSecret: abcdefghijklmnopqrstuvwxyz0123456789ABCDEFG=
gcpAttachments.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-----
apn.teamId: team-id
apn.keyId: key-id
apn.signingKey: |
-----BEGIN PRIVATE KEY-----
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
AAAAAAAA
-----END PRIVATE KEY-----
fcm.credentials: |
{ "json": true }
cdn.accessKey: test # AWS Access Key ID
cdn.accessSecret: test # AWS Access Secret
cdn3StorageManager.clientSecret: test
unidentifiedDelivery.privateKey: ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789AAAAAAA
keyTransparencyService.clientPrivateKey: |
-----BEGIN PRIVATE KEY-----
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
AAAAAAAA
-----END PRIVATE KEY-----
storageService.userAuthenticationTokenSharedSecret: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
zkConfig-libsignal-0.42.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+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+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+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdef
genericZkConfig.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==
callingZkConfig.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==
backupsZkConfig.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==
paymentsService.userAuthenticationTokenSharedSecret: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= # base64-encoded 32-byte secret shared with MobileCoin services used to generate auth tokens for Signal users
paymentsService.fixerApiKey: unset
paymentsService.coinGeckoApiKey: unset
currentReportingKey.secret: AAAAAAAAAAA=
currentReportingKey.salt: AAAAAAAAAAA=
registrationService.collationKeySalt: AAAAAAAAAAA=
turn.cloudflare.apiToken: ABCDEFGHIJKLM
linkDevice.secret: AAAAAAAAAAA=
tlsKeyStore.password: unset
hlrLookup.apiKey: AAAAAAAAAAA
hlrLookup.apiSecret: AAAAAAAAAAA
foundationDbMessages.versionstampCipherKey.0: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=

View File

@ -1,567 +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.
logging:
level: INFO
appenders:
- type: console
threshold: ALL
timeZone: UTC
target: stdout
- type: otlp
tlsKeyStore:
password: secret://tlsKeyStore.password
stripe:
apiKey: secret://stripe.apiKey
idempotencyKeyGenerator: secret://stripe.idempotencyKeyGenerator
boostDescription: >
Example
supportedCurrenciesByPaymentMethod:
CARD:
- usd
- eur
SEPA_DEBIT:
- eur
braintree:
merchantId: unset
publicKey: secret://braintree.publicKey
privateKey: secret://braintree.privateKey
environment: unset
graphqlUrl: unset
merchantAccounts:
# ISO 4217 currency code and its corresponding sub-merchant account
'xts': unset
supportedCurrenciesByPaymentMethod:
PAYPAL:
- usd
pubSubPublisher:
project: example-project
topic: example-topic
credentialConfiguration: |
{
"credential": "configuration"
}
googlePlayBilling:
credentialsJson: |
{
"credential": "configuration"
}
packageName: package.name
applicationName: test
productIdToLevel: {}
appleAppStore:
env: SANDBOX
bundleId: bundle.name
appAppleId: 12345
issuerId: abcdefg
keyId: abcdefg
encodedKey: secret://appleAppStore.encodedKey
subscriptionGroupId: example_subscriptionGroupId
productIdToLevel: {}
appleRootCerts: []
appleDeviceCheck:
production: false
teamId: 0123456789
bundleId: bundle.name
deviceCheck:
backupRedemptionDuration: P30D
backupRedemptionLevel: 201
dynamoDbClient:
region: us-west-2 # AWS Region
dynamoDbTables:
accounts:
tableName: Example_Accounts
phoneNumberTableName: Example_Accounts_PhoneNumbers
phoneNumberIdentifierTableName: Example_Accounts_PhoneNumberIdentifiers
usernamesTableName: Example_Accounts_Usernames
usedLinkDeviceTokensTableName: Example_Accounts_UsedLinkDeviceTokens
appleDeviceChecks:
tableName: Example_AppleDeviceChecks
appleDeviceCheckPublicKeys:
tableName: Example_AppleDeviceCheckPublicKeys
backups:
tableName: Example_Backups
changeNumberWaitingPeriods:
tableName: Example_ChangeNumberWaitingPeriods
clientReleases:
tableName: Example_ClientReleases
deletedAccounts:
tableName: Example_DeletedAccounts
deletedAccountsLock:
tableName: Example_DeletedAccountsLock
donationPermits:
tableName: Example_DonationPermits
expiration: P7D # Duration of time until rows expire
issuedReceipts:
tableName: Example_IssuedReceipts
expiration: P30D # Duration of time until rows expire
generator: abcdefg12345678= # random base64-encoded binary sequence
maxIssuedReceiptsPerPaymentId:
STRIPE: 1
BRAINTREE: 1
GOOGLE_PLAY_BILLING: 1
APPLE_APP_STORE: 1
ecKeys:
tableName: Example_Keys
ecSignedPreKeys:
tableName: Example_EC_Signed_Pre_Keys
pagedPqKeys:
tableName: Example_PQ_Paged_Keys
pqLastResortKeys:
tableName: Example_PQ_Last_Resort_Keys
messages:
tableName: Example_Messages
expiration: P30D # Duration of time until rows expire
onetimeDonations:
tableName: Example_OnetimeDonations
expiration: P90D
phoneNumberIdentifiers:
tableName: Example_PhoneNumberIdentifiers
profiles:
tableName: Example_Profiles
profilesV2:
tableName: Example_ProfilesV2
pushChallenge:
tableName: Example_PushChallenge
pushNotificationExperimentSamples:
tableName: Example_PushNotificationExperimentSamples
redeemedReceipts:
tableName: Example_RedeemedReceipts
expiration: P30D # Duration of time until rows expire
registrationRecovery:
tableName: Example_RegistrationRecovery
expiration: P300D # Duration of time until rows expire
remoteConfig:
tableName: Example_RemoteConfig
reportMessage:
tableName: Example_ReportMessage
scheduledJobs:
tableName: Example_ScheduledJobs
expiration: P7D
subscriptions:
tableName: Example_Subscriptions
verificationSessions:
tableName: Example_VerificationSessions
pagedSingleUseKEMPreKeyStore:
bucket: preKeyBucket # S3 Bucket name
region: us-west-2 # AWS region
cacheCluster: # Redis server configuration for cache cluster
configurationUri: redis://redis.example.com:6379/
pubsub: # Redis server configuration for pubsub cluster
uri: 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/
directoryV2:
client: # Configuration for interfacing with Contact Discovery Service v2 cluster
userAuthenticationTokenSharedSecret: secret://directoryV2.client.userAuthenticationTokenSharedSecret
userIdTokenSharedSecret: secret://directoryV2.client.userIdTokenSharedSecret
svr2:
uri: svr2.example.com
userAuthenticationTokenSharedSecret: secret://svr2.userAuthenticationTokenSharedSecret
userIdTokenSharedSecret: secret://svr2.userIdTokenSharedSecret
svrCaCertificates:
- |
-----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-----
svrb:
uri: svrb.example.com
userAuthenticationTokenSharedSecret: secret://svrb.userAuthenticationTokenSharedSecret
userIdTokenSharedSecret: secret://svrb.userIdTokenSharedSecret
svrCaCertificates:
- |
-----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-----
messageCache: # Redis server configuration for message store cache
persistDelayMinutes: 1
cluster:
configurationUri: redis://redis.example.com:6379/
attachments:
maxAttachmentUploadSizeInBytes: 1024
maxMessageBackupUploadSizeInBytes: 1024
gcpAttachments: # GCP Storage configuration
domain: example.com
email: user@example.cocm
pathPrefix:
rsaSigningKey: secret://gcpAttachments.rsaSigningKey
tus:
uploadUri: https://example.org/upload
userAuthenticationTokenSharedSecret: secret://tus.userAuthenticationTokenSharedSecret
apn: # Apple Push Notifications configuration
sandbox: true
bundleId: com.example.textsecuregcm
keyId: secret://apn.keyId
teamId: secret://apn.teamId
signingKey: secret://apn.signingKey
fcm: # FCM configuration
credentials: secret://fcm.credentials
cdn:
bucket: cdn # S3 Bucket name
credentials:
accessKeyId: secret://cdn.accessKey
secretAccessKey: secret://cdn.accessSecret
region: us-west-2 # AWS region
cdn3StorageManager:
baseUri: https://storage-manager.example.com
clientId: example
clientSecret: secret://cdn3StorageManager.clientSecret
sourceSchemes:
2: gcs
3: r2
openTelemetry:
enabled: true
environment: dev
url: http://127.0.0.1:4318/
unidentifiedDelivery:
certificate: CgIIAQ==
privateKey: secret://unidentifiedDelivery.privateKey
expiresDays: 7
embedSigner: true
shortCode:
baseUrl: https://example.com/shortcodes/
storageService:
uri: storage.example.com
userAuthenticationTokenSharedSecret: secret://storageService.userAuthenticationTokenSharedSecret
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-----
zkConfig:
serverPublic: 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+abcdefghijklmnopqrstuvwxyzAB==
serverSecret: secret://zkConfig-libsignal-0.42.serverSecret
callingZkConfig:
serverSecret: secret://callingZkConfig.serverSecret
backupsZkConfig:
serverSecret: secret://backupsZkConfig.serverSecret
dynamicConfig:
s3Region: a-region
s3Bucket: a-bucket
objectKey: dynamic-config.yaml
maxSize: 100000
refreshInterval: PT10S
remoteConfig:
globalConfig: # keys and values that are given to clients on GET /v1/config
EXAMPLE_KEY: VALUE
paymentsService:
userAuthenticationTokenSharedSecret: secret://paymentsService.userAuthenticationTokenSharedSecret
paymentCurrencies:
# list of symbols for supported currencies
- MOB
externalClients:
fixerApiKey: secret://paymentsService.fixerApiKey
coinGeckoApiKey: secret://paymentsService.coinGeckoApiKey
coinGeckoCurrencyIds:
MOB: mobilecoin
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
badgeExpiration: P30D
badgeGracePeriod: P15D
backupExpiration: P30D
backupGracePeriod: P15D
backupFreeTierMediaDuration: P30D
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:
sepaMaximumEuros: '10000'
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
port: 443
credentialConfigurationJson: |
{
"example": "example"
}
identityTokenAudience: https://registration.example.com
collationKeySalt: secret://registrationService.collationKeySalt
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-----
keyTransparencyService:
host: kt.example.com
port: 443
tlsCertificate: |
-----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-----
clientCertificate: |
-----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-----
clientPrivateKey: secret://keyTransparencyService.clientPrivateKey
turn:
cloudflare:
apiToken: secret://turn.cloudflare.apiToken
endpoint: https://rtc.live.cloudflare.com/v1/turn/keys/LMNOP/credentials/generate
urls:
- turn:turn.example.com:80
urlsWithIps:
- turn:%s
- turn:%s:80?transport=tcp
- turns:%s:443?transport=tcp
requestedCredentialTtl: PT24H
clientCredentialTtl: PT12H
hostname: turn.cloudflare.example.com
numHttpClients: 1
linkDevice:
secret: secret://linkDevice.secret
externalRequestFilter:
grpcMethods:
- com.example.grpc.ExampleService/exampleMethod
paths:
- /example
permittedInternalRanges:
- 127.0.0.0/8
idlePrimaryDeviceReminder:
minIdleDuration: P30D
grpc:
port: 50051
websocketPort: 8080
asnTable:
s3Region: a-region
s3Bucket: a-bucket
objectKey: asn.tsv
maxSize: 100000
refreshInterval: PT10S
callQualitySurvey:
pubSubPublisher:
project: example-project
topic: example-topic
credentialConfiguration: |
{
"credential": "configuration"
}
hlrLookup:
apiKey: secret://hlrLookup.apiKey
apiSecret: secret://hlrLookup.apiSecret
foundationDbMessages:
maxWatchesPerClient: 10000
versionstampCipherKeys:
0: secret://foundationDbMessages.versionstampCipherKey.0
currentVersionstampCipherKey: 0
clusters:
"messages-0":
clusterFileUrl: http://clusterfiles.example.com/messages-0
"messages-1":
clusterFileUrl: http://clusterfiles.example.com/messages-1
"messages-2":
clusterFileUrl: http://clusterfiles.example.com/messages-2
"messages-3":
clusterFileUrl: http://clusterfiles.example.com/messages-3
epochs:
0:
- messages-0
- messages-1
1:
- messages-0
- messages-1
- messages-2
- messages-3

View File

@ -1,909 +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>
<properties>
<apollo-api-jvm.version>3.8.5</apollo-api-jvm.version>
<commons-compress.version>1.28.0</commons-compress.version>
<dynamodb-lock-client.version>1.4.0</dynamodb-lock-client.version>
<firebase-admin.version>9.7.0</firebase-admin.version>
<libphonenumber-geocoder.version>3.23</libphonenumber-geocoder.version>
<google-androidpublisher.version>v3-rev20250904-2.0.0</google-androidpublisher.version>
<java-jwt.version>4.5.0</java-jwt.version>
<java-uuid-generator.version>5.2.0</java-uuid-generator.version>
<!-- *all* opentelemetry-logback-appender versions are "alpha" despite the advanced version number -->
<opentelemetry-logback-appender-1.0.version>2.22.0-alpha</opentelemetry-logback-appender-1.0.version>
<storekit.version>4.0.0</storekit.version>
<webauthn4j.version>0.30.2.RELEASE</webauthn4j.version>
<jetty.http2-client.version>12.1.5</jetty.http2-client.version>
</properties>
<dependencies>
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>${java-jwt.version}</version>
</dependency>
<dependency>
<groupId>com.google.apis</groupId>
<artifactId>google-api-services-androidpublisher</artifactId>
<version>${google-androidpublisher.version}</version>
</dependency>
<dependency>
<groupId>com.apple.itunes.storekit</groupId>
<artifactId>app-store-server-library</artifactId>
<version>${storekit.version}</version>
<exclusions>
<!-- conflicts with other users; resolved manually with explicit import -->
<exclusion>
<groupId>com.squareup.okio</groupId>
<artifactId>okio-jvm</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.webauthn4j</groupId>
<artifactId>webauthn4j-appattest</artifactId>
<version>${webauthn4j.version}</version>
</dependency>
<dependency>
<groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-jaxrs2-jakarta</artifactId>
<version>${swagger.version}</version>
<exclusions>
<!-- conflicts with jackson-dataformat-yaml -->
<exclusion>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
</exclusion>
</exclusions>
</dependency>
<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>websocket-resources</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.signal</groupId>
<artifactId>libsignal-server</artifactId>
</dependency>
<dependency>
<groupId>org.signal</groupId>
<artifactId>simple-grpc-runtime</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-http2</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>org.eclipse.jetty.http2</groupId>
<artifactId>jetty-http2-client-transport</artifactId>
<scope>test</scope>
<version>${jetty.http2-client.version}</version>
</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.access</groupId>
<artifactId>logback-access-common</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>io.dropwizard</groupId>
<artifactId>dropwizard-testing</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-sdk</artifactId>
</dependency>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-exporter-otlp</artifactId>
</dependency>
<dependency>
<groupId>io.opentelemetry.instrumentation</groupId>
<artifactId>opentelemetry-logback-appender-1.0</artifactId>
<version>${opentelemetry-logback-appender-1.0.version}</version>
<exclusions>
<!-- incubator packages aren't included in the opentelemetry BOM, and we don't use them -->
<exclusion>
<groupId>io.opentelemetry.instrumentation</groupId>
<artifactId>opentelemetry-instrumentation-api-incubator</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>party.iroiro.luajava</groupId>
<artifactId>luajava</artifactId>
<version>${luajava.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>party.iroiro.luajava</groupId>
<artifactId>lua51</artifactId>
<version>${luajava.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>party.iroiro.luajava</groupId>
<artifactId>lua51-platform</artifactId>
<version>${luajava.version}</version>
<classifier>natives-desktop</classifier>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.websocket</groupId>
<artifactId>jetty-websocket-jetty-api</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.ee10</groupId>
<artifactId>jetty-ee10-servlets</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.websocket</groupId>
<artifactId>jetty-websocket-jetty-client</artifactId>
<scope>test</scope>
</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>org.apache.commons</groupId>
<artifactId>commons-compress</artifactId>
<version>${commons-compress.version}</version>
</dependency>
<dependency>
<groupId>com.google.cloud</groupId>
<artifactId>google-cloud-pubsub</artifactId>
</dependency>
<dependency>
<groupId>com.google.firebase</groupId>
<artifactId>firebase-admin</artifactId>
<version>${firebase-admin.version}</version>
</dependency>
<dependency>
<groupId>com.google.cloud</groupId>
<artifactId>google-cloud-firestore</artifactId>
<exclusions>
<!-- incubator packages aren't included in the opentelemetry BOM, and we don't use them -->
<exclusion>
<groupId>io.opentelemetry.instrumentation</groupId>
<artifactId>opentelemetry-instrumentation-api-incubator</artifactId>
</exclusion>
</exclusions>
</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</artifactId>
</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-otlp</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>org.foundationdb</groupId>
<artifactId>fdb-java</artifactId>
</dependency>
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>apache-client</artifactId>
</dependency>
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>netty-nio-client</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>dynamodb</artifactId>
</dependency>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>dynamodb-lock-client</artifactId>
<version>${dynamodb-lock-client.version}</version>
</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>
<!-- Provides tools for mapping phone numbers to time zones, which is helpful for scheduling push notifications
during waking hours -->
<dependency>
<groupId>com.googlecode.libphonenumber</groupId>
<artifactId>geocoder</artifactId>
<version>${libphonenumber-geocoder.version}</version>
</dependency>
<dependency>
<groupId>net.sourceforge.argparse4j</groupId>
<artifactId>argparse4j</artifactId>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-buffer</artifactId>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-codec-haproxy</artifactId>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-codec-http2</artifactId>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-handler</artifactId>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-common</artifactId>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-resolver</artifactId>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-resolver-dns</artifactId>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-transport</artifactId>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-transport-native-epoll</artifactId>
<classifier>linux-x86_64</classifier>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-testsuite-common</artifactId>
<scope>test</scope>
<version>${netty.version}</version>
</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>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core-micrometer</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>com.redis</groupId>
<artifactId>testcontainers-redis</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.uuid</groupId>
<artifactId>java-uuid-generator</artifactId>
<version>${java-uuid-generator.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers-localstack</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers-junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>earth.adi</groupId>
<artifactId>testcontainers-foundationdb</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.auth</groupId>
<artifactId>google-auth-library-oauth2-http</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>${apollo-api-jvm.version}</version>
<exclusions>
<exclusion>
<groupId>org.jetbrains</groupId>
<artifactId>annotations</artifactId>
</exclusion>
<!-- conflicts with other users; resolved manually with explicit import -->
<exclusion>
<groupId>com.squareup.okio</groupId>
<artifactId>okio-jvm</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- to resolve conflicting imports from other dependencies -->
<dependency>
<groupId>com.squareup.okio</groupId>
<artifactId>okio-jvm</artifactId>
<version>3.16.4</version>
</dependency>
</dependencies>
<profiles>
<profile>
<id>exclude-spam-filter</id>
<build>
<plugins>
<plugin>
<groupId>io.github.download-maven-plugin</groupId>
<artifactId>download-maven-plugin</artifactId>
<version>2.1.0</version>
<executions>
<execution>
<id>install-foundationdb-client-library</id>
<phase>prepare-package</phase>
<goals>
<goal>wget</goal>
</goals>
</execution>
</executions>
<configuration>
<url>https://github.com/apple/foundationdb/releases/download/${foundationdb.version}/libfdb_c.x86_64.so</url>
<outputDirectory>${project.build.directory}/jib-extra/usr/lib</outputDirectory>
<outputFileName>libfdb_c.so</outputFileName>
<sha256>${foundationdb.client-library-sha256}</sha256>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<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>
<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>
<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>com.google.cloud.tools</groupId>
<artifactId>jib-maven-plugin</artifactId>
<executions>
<execution>
<phase>deploy</phase>
<goals>
<goal>build</goal>
</goals>
</execution>
</executions>
<configuration>
<from>
<image>eclipse-temurin@sha256:${docker.image.sha256}</image>
<platforms>
<platform>
<architecture>amd64</architecture>
<os>linux</os>
</platform>
<platform>
<architecture>arm64</architecture>
<os>linux</os>
</platform>
</platforms>
</from>
<to>
<image>${docker.repo}:${project.version}</image>
</to>
<container>
<mainClass>org.whispersystems.textsecuregcm.WhisperServerService</mainClass>
<jvmFlags>
<jvmFlag>-server</jvmFlag>
<jvmFlag>-Djava.awt.headless=true</jvmFlag>
<jvmFlag>-Djdk.nio.maxCachedBufferSize=262144</jvmFlag>
<jvmFlag>-Dlog4j2.formatMsgNoLookups=true</jvmFlag>
<jvmFlag>-Djdk.tls.server.newSessionTicketCount=0</jvmFlag>
<jvmFlag>-XX:MaxRAMPercentage=75</jvmFlag>
<jvmFlag>-XX:+HeapDumpOnOutOfMemoryError</jvmFlag>
<jvmFlag>-XX:HeapDumpPath=/tmp/heapdump.bin</jvmFlag>
<!-- This will likely become the default in a future version of Java, and we can remove this flag when
that happens. Please see https://openjdk.org/jeps/534 for details. -->
<jvmFlag>-XX:+UseCompactObjectHeaders</jvmFlag>
</jvmFlags>
<ports>
<port>8080</port>
</ports>
<creationTime>USE_CURRENT_TIMESTAMP</creationTime>
</container>
<extraDirectories>
<paths>
<path>
<from>${project.basedir}/config</from>
<includes>*.yml</includes>
<into>/usr/share/signal/</into>
</path>
<path>
<from>${project.build.directory}/jib-extra</from>
</path>
</paths>
</extraDirectories>
</configuration>
</plugin>
</plugins>
</build>
</profile>
<profile>
<id>include-spam-filter</id>
<build>
<plugins>
<plugin>
<groupId>com.google.cloud.tools</groupId>
<artifactId>jib-maven-plugin</artifactId>
<configuration>
<!-- we don't want jib to execute on this module -->
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>
</profile>
<profile>
<id>test-server</id>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<executions>
<execution>
<id>start-test-server</id>
<phase>integration-test</phase>
<goals>
<goal>java</goal>
</goals>
<configuration>
<mainClass>org.whispersystems.textsecuregcm.LocalWhisperServerService</mainClass>
<classpathScope>test</classpathScope>
</configuration>
</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>3.0.0</version>
<executions>
<execution>
<id>filter-src</id>
<goals>
<goal>filter-sources</goal>
</goals>
</execution>
<execution>
<id>filter-test-src</id>
<goals>
<goal>filter-test-sources</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<!-- add-opens: work around PATCH not being a supported method on HttpUrlConnection -->
<argLine>-javaagent:${org.mockito:mockito-core:jar} --add-opens=java.base/java.net=ALL-UNNAMED</argLine>
</configuration>
</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>
<executions>
<execution>
<id>check-all-service-config</id>
<phase>verify</phase>
<goals>
<goal>java</goal>
</goals>
<configuration>
<mainClass>org.whispersystems.textsecuregcm.CheckServiceConfigurations</mainClass>
<classpathScope>test</classpathScope>
<arguments>
<argument>${project.basedir}/config</argument>
</arguments>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>com.github.aoudiamoncef</groupId>
<artifactId>apollo-client-maven-plugin</artifactId>
<version>7.1.0</version>
<executions>
<execution>
<goals>
<goal>generate</goal>
</goals>
<configuration>
<services>
<braintree>
<compilationUnit>
<name>braintree</name>
<compilerParams>
<schemaPackageName>com.braintree.graphql.client</schemaPackageName>
<!-- override the default in 7.1.0, see https://github.com/aoudiamoncef/apollo-client-maven-plugin/issues/84 -->
<operationManifestFormat>none</operationManifestFormat>
</compilerParams>
</compilationUnit>
</braintree>
</services>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@ -1,9 +0,0 @@
# https://graphql.braintreepayments.com/reference/#Mutation--chargePaymentMethod
mutation ChargePayPalOneTimePayment($input: ChargePaymentMethodInput!) {
chargePaymentMethod(input: $input) {
transaction {
id,
status
}
}
}

View File

@ -1,6 +0,0 @@
mutation CreatePayPalBillingAgreement($input: CreatePayPalBillingAgreementInput!) {
createPayPalBillingAgreement(input: $input) {
approvalUrl,
billingAgreementToken
}
}

View File

@ -1,7 +0,0 @@
# https://graphql.braintreepayments.com/reference/#Mutation--createPayPalOneTimePayment
mutation CreatePayPalOneTimePayment($input: CreatePayPalOneTimePaymentInput!) {
createPayPalOneTimePayment(input: $input) {
approvalUrl,
paymentId
}
}

View File

@ -1,7 +0,0 @@
mutation TokenizePayPalBillingAgreement($input: TokenizePayPalBillingAgreementInput!) {
tokenizePayPalBillingAgreement(input: $input) {
paymentMethod {
id
}
}
}

View File

@ -1,8 +0,0 @@
# https://graphql.braintreepayments.com/reference/#Mutation--tokenizePayPalOneTimePayment
mutation TokenizePayPalOneTimePayment($input: TokenizePayPalOneTimePaymentInput!) {
tokenizePayPalOneTimePayment(input: $input) {
paymentMethod {
id
}
}
}

View File

@ -1,7 +0,0 @@
mutation VaultPaymentMethod($input: VaultPaymentMethodInput!) {
vaultPaymentMethod(input: $input) {
paymentMethod {
id
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -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;
}
}

View File

@ -1,20 +0,0 @@
/*
* Copyright 2025 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.storage;
public class FoundationDbVersion {
private static final String VERSION = "${foundationdb.version}";
private static final int API_VERSION = ${foundationdb.api-version};
public static String getFoundationDbVersion() {
return VERSION;
}
public static int getFoundationDbApiVersion() {
return API_VERSION;
}
}

View File

@ -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);
}
}

View File

@ -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);
}

View File

@ -1,613 +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.core.Configuration;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotNull;
import java.time.Duration;
import java.util.Collections;
import java.util.Map;
import org.whispersystems.textsecuregcm.attachments.TusConfiguration;
import org.whispersystems.textsecuregcm.configuration.ApnConfiguration;
import org.whispersystems.textsecuregcm.configuration.AppleAppStoreConfiguration;
import org.whispersystems.textsecuregcm.configuration.AppleDeviceCheckConfiguration;
import org.whispersystems.textsecuregcm.configuration.AttachmentsConfiguration;
import org.whispersystems.textsecuregcm.configuration.AwsCredentialsProviderFactory;
import org.whispersystems.textsecuregcm.configuration.BadgesConfiguration;
import org.whispersystems.textsecuregcm.configuration.BraintreeConfiguration;
import org.whispersystems.textsecuregcm.configuration.CallQualitySurveyConfiguration;
import org.whispersystems.textsecuregcm.configuration.Cdn3StorageManagerConfiguration;
import org.whispersystems.textsecuregcm.configuration.CdnConfiguration;
import org.whispersystems.textsecuregcm.configuration.ChangeNumberConfiguration;
import org.whispersystems.textsecuregcm.configuration.CircuitBreakerConfiguration;
import org.whispersystems.textsecuregcm.configuration.ClientReleaseConfiguration;
import org.whispersystems.textsecuregcm.configuration.DefaultAwsCredentialsFactory;
import org.whispersystems.textsecuregcm.configuration.DeviceCheckConfiguration;
import org.whispersystems.textsecuregcm.configuration.DirectoryV2Configuration;
import org.whispersystems.textsecuregcm.configuration.DynamoDbClientFactory;
import org.whispersystems.textsecuregcm.configuration.DynamoDbTables;
import org.whispersystems.textsecuregcm.configuration.ExternalRequestFilterConfiguration;
import org.whispersystems.textsecuregcm.configuration.FaultTolerantRedisClientFactory;
import org.whispersystems.textsecuregcm.configuration.FaultTolerantRedisClusterFactory;
import org.whispersystems.textsecuregcm.configuration.FcmConfiguration;
import org.whispersystems.textsecuregcm.configuration.FoundationDbMessagesConfiguration;
import org.whispersystems.textsecuregcm.configuration.GcpAttachmentsConfiguration;
import org.whispersystems.textsecuregcm.configuration.GenericZkConfig;
import org.whispersystems.textsecuregcm.configuration.GooglePlayBillingConfiguration;
import org.whispersystems.textsecuregcm.configuration.GrpcConfiguration;
import org.whispersystems.textsecuregcm.configuration.HlrLookupConfiguration;
import org.whispersystems.textsecuregcm.configuration.IdlePrimaryDeviceReminderConfiguration;
import org.whispersystems.textsecuregcm.configuration.KeyTransparencyServiceConfiguration;
import org.whispersystems.textsecuregcm.configuration.LinkDeviceSecretConfiguration;
import org.whispersystems.textsecuregcm.configuration.MessageByteLimitCardinalityEstimatorConfiguration;
import org.whispersystems.textsecuregcm.configuration.MessageCacheConfiguration;
import org.whispersystems.textsecuregcm.configuration.OneTimeDonationConfiguration;
import org.whispersystems.textsecuregcm.configuration.OpenTelemetryConfiguration;
import org.whispersystems.textsecuregcm.configuration.PagedSingleUseKEMPreKeyStoreConfiguration;
import org.whispersystems.textsecuregcm.configuration.PaymentsServiceConfiguration;
import org.whispersystems.textsecuregcm.configuration.RegistrationServiceClientFactory;
import org.whispersystems.textsecuregcm.configuration.RemoteConfigConfiguration;
import org.whispersystems.textsecuregcm.configuration.ReportMessageConfiguration;
import org.whispersystems.textsecuregcm.configuration.RetryConfiguration;
import org.whispersystems.textsecuregcm.configuration.S3ObjectMonitorFactory;
import org.whispersystems.textsecuregcm.configuration.SecureStorageServiceConfiguration;
import org.whispersystems.textsecuregcm.configuration.SecureValueRecoveryConfiguration;
import org.whispersystems.textsecuregcm.configuration.ShortCodeExpanderConfiguration;
import org.whispersystems.textsecuregcm.configuration.SpamFilterConfiguration;
import org.whispersystems.textsecuregcm.configuration.StripeConfiguration;
import org.whispersystems.textsecuregcm.configuration.SubscriptionConfiguration;
import org.whispersystems.textsecuregcm.configuration.TlsKeyStoreConfiguration;
import org.whispersystems.textsecuregcm.configuration.TurnConfiguration;
import org.whispersystems.textsecuregcm.configuration.UnidentifiedDeliveryConfiguration;
import org.whispersystems.textsecuregcm.configuration.VirtualThreadConfiguration;
import org.whispersystems.textsecuregcm.configuration.ZkConfig;
import org.whispersystems.websocket.configuration.WebSocketConfiguration;
/** @noinspection MismatchedQueryAndUpdateOfCollection, WeakerAccess */
public class WhisperServerConfiguration extends Configuration {
@NotNull
@Valid
@JsonProperty
private TlsKeyStoreConfiguration tlsKeyStore;
@NotNull
@Valid
@JsonProperty
AwsCredentialsProviderFactory awsCredentialsProvider = new DefaultAwsCredentialsFactory();
@NotNull
@Valid
@JsonProperty
private StripeConfiguration stripe;
@NotNull
@Valid
@JsonProperty
private BraintreeConfiguration braintree;
@NotNull
@Valid
@JsonProperty
private GooglePlayBillingConfiguration googlePlayBilling;
@NotNull
@Valid
@JsonProperty
private AppleAppStoreConfiguration appleAppStore;
@NotNull
@Valid
@JsonProperty
private AppleDeviceCheckConfiguration appleDeviceCheck;
@NotNull
@Valid
@JsonProperty
private DeviceCheckConfiguration deviceCheck;
@NotNull
@Valid
@JsonProperty
private DynamoDbClientFactory dynamoDbClient;
@NotNull
@Valid
@JsonProperty
private DynamoDbTables dynamoDbTables;
@NotNull
@Valid
@JsonProperty
private AttachmentsConfiguration attachments;
@NotNull
@Valid
@JsonProperty
private GcpAttachmentsConfiguration gcpAttachments;
@NotNull
@Valid
@JsonProperty
private CdnConfiguration cdn;
@NotNull
@Valid
@JsonProperty
private Cdn3StorageManagerConfiguration cdn3StorageManager;
@NotNull
@Valid
@JsonProperty
private OpenTelemetryConfiguration openTelemetry;
@NotNull
@Valid
@JsonProperty
private FaultTolerantRedisClusterFactory cacheCluster;
@NotNull
@Valid
@JsonProperty
private FaultTolerantRedisClientFactory pubsub;
@NotNull
@Valid
@JsonProperty
private DirectoryV2Configuration directoryV2;
@NotNull
@Valid
@JsonProperty
private SecureValueRecoveryConfiguration svr2;
@NotNull
@Valid
@JsonProperty
private SecureValueRecoveryConfiguration svrb;
@NotNull
@Valid
@JsonProperty
private FaultTolerantRedisClusterFactory pushSchedulerCluster;
@NotNull
@Valid
@JsonProperty
private FaultTolerantRedisClusterFactory rateLimitersCluster;
@NotNull
@Valid
@JsonProperty
private MessageCacheConfiguration messageCache;
@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 ShortCodeExpanderConfiguration shortCode;
@Valid
@NotNull
@JsonProperty
private SecureStorageServiceConfiguration storageService;
@Valid
@NotNull
@JsonProperty
private PaymentsServiceConfiguration paymentsService;
@Valid
@NotNull
@JsonProperty
private ZkConfig zkConfig;
@Valid
@NotNull
@JsonProperty
private GenericZkConfig callingZkConfig;
@Valid
@NotNull
@JsonProperty
private GenericZkConfig backupsZkConfig;
@Valid
@NotNull
@JsonProperty
private RemoteConfigConfiguration remoteConfig;
@Valid
@NotNull
@JsonProperty
private S3ObjectMonitorFactory dynamicConfig;
@Valid
@NotNull
@JsonProperty
private BadgesConfiguration badges;
@Valid
@JsonProperty
@NotNull
private SubscriptionConfiguration subscription;
@Valid
@JsonProperty
@NotNull
private OneTimeDonationConfiguration oneTimeDonations;
@Valid
@JsonProperty
@NotNull
private PagedSingleUseKEMPreKeyStoreConfiguration pagedSingleUseKEMPreKeyStore;
@Valid
@NotNull
@JsonProperty
private ReportMessageConfiguration reportMessage = new ReportMessageConfiguration();
@Valid
@JsonProperty
private SpamFilterConfiguration spamFilter;
@Valid
@NotNull
@JsonProperty
private RegistrationServiceClientFactory registrationService;
@Valid
@NotNull
@JsonProperty
private TurnConfiguration turn;
@Valid
@NotNull
@JsonProperty
private TusConfiguration tus;
@Valid
@NotNull
@JsonProperty
private ClientReleaseConfiguration clientRelease = new ClientReleaseConfiguration(Duration.ofHours(4));
@Valid
@NotNull
@JsonProperty
private MessageByteLimitCardinalityEstimatorConfiguration messageByteLimitCardinalityEstimator = new MessageByteLimitCardinalityEstimatorConfiguration(Duration.ofDays(1));
@Valid
@NotNull
@JsonProperty
private LinkDeviceSecretConfiguration linkDevice;
@Valid
@NotNull
@JsonProperty
private VirtualThreadConfiguration virtualThread = new VirtualThreadConfiguration();
@Valid
@NotNull
@JsonProperty
private ExternalRequestFilterConfiguration externalRequestFilter;
@Valid
@NotNull
@JsonProperty
private KeyTransparencyServiceConfiguration keyTransparencyService;
@JsonProperty
private boolean logMessageDeliveryLoops;
@JsonProperty
private IdlePrimaryDeviceReminderConfiguration idlePrimaryDeviceReminder =
new IdlePrimaryDeviceReminderConfiguration(Duration.ofDays(30));
@JsonProperty
private Map<String, @Valid CircuitBreakerConfiguration> circuitBreakers = Collections.emptyMap();
@JsonProperty
private Map<String, @Valid RetryConfiguration> retries = Collections.emptyMap();
@Valid
@NotNull
@JsonProperty
private HlrLookupConfiguration hlrLookup;
@JsonProperty
@Valid
@NotNull
private RetryConfiguration generalRedisRetry = new RetryConfiguration();
@NotNull
@Valid
@JsonProperty
private GrpcConfiguration grpc;
@Valid
@NotNull
@JsonProperty
private S3ObjectMonitorFactory asnTable;
@Valid
@NotNull
@JsonProperty
private CallQualitySurveyConfiguration callQualitySurvey;
@Valid
@NotNull
@JsonProperty
private ChangeNumberConfiguration changeNumber = new ChangeNumberConfiguration(Duration.ofHours(1));
@Valid
@NotNull
@JsonProperty
private FoundationDbMessagesConfiguration foundationDbMessages;
public TlsKeyStoreConfiguration getTlsKeyStoreConfiguration() {
return tlsKeyStore;
}
public AwsCredentialsProviderFactory getAwsCredentialsConfiguration() {
return awsCredentialsProvider;
}
public StripeConfiguration getStripe() {
return stripe;
}
public BraintreeConfiguration getBraintree() {
return braintree;
}
public GooglePlayBillingConfiguration getGooglePlayBilling() {
return googlePlayBilling;
}
public AppleAppStoreConfiguration getAppleAppStore() {
return appleAppStore;
}
public AppleDeviceCheckConfiguration getAppleDeviceCheck() {
return appleDeviceCheck;
}
public DeviceCheckConfiguration getDeviceCheck() {
return deviceCheck;
}
public DynamoDbClientFactory getDynamoDbClientConfiguration() {
return dynamoDbClient;
}
public DynamoDbTables getDynamoDbTables() {
return dynamoDbTables;
}
public ShortCodeExpanderConfiguration getShortCodeRetrieverConfiguration() {
return shortCode;
}
public WebSocketConfiguration getWebSocketConfiguration() {
return webSocket;
}
public AttachmentsConfiguration getAttachments() {
return attachments;
}
public GcpAttachmentsConfiguration getGcpAttachmentsConfiguration() {
return gcpAttachments;
}
public FaultTolerantRedisClusterFactory getCacheClusterConfiguration() {
return cacheCluster;
}
public FaultTolerantRedisClientFactory getRedisPubSubConfiguration() {
return pubsub;
}
public SecureValueRecoveryConfiguration getSvr2Configuration() {
return svr2;
}
public SecureValueRecoveryConfiguration getSvrbConfiguration() {
return svrb;
}
public DirectoryV2Configuration getDirectoryV2Configuration() {
return directoryV2;
}
public SecureStorageServiceConfiguration getSecureStorageServiceConfiguration() {
return storageService;
}
public MessageCacheConfiguration getMessageCacheConfiguration() {
return messageCache;
}
public FaultTolerantRedisClusterFactory getPushSchedulerCluster() {
return pushSchedulerCluster;
}
public FaultTolerantRedisClusterFactory getRateLimitersCluster() {
return rateLimitersCluster;
}
public FcmConfiguration getFcmConfiguration() {
return fcm;
}
public ApnConfiguration getApnConfiguration() {
return apn;
}
public CdnConfiguration getCdnConfiguration() {
return cdn;
}
public Cdn3StorageManagerConfiguration getCdn3StorageManagerConfiguration() {
return cdn3StorageManager;
}
public OpenTelemetryConfiguration getOpenTelemetryConfiguration() {
return openTelemetry;
}
public UnidentifiedDeliveryConfiguration getDeliveryCertificate() {
return unidentifiedDelivery;
}
public PaymentsServiceConfiguration getPaymentsServiceConfiguration() {
return paymentsService;
}
public ZkConfig getZkConfig() {
return zkConfig;
}
public GenericZkConfig getCallingZkConfig() {
return callingZkConfig;
}
public GenericZkConfig getBackupsZkConfig() {
return backupsZkConfig;
}
public RemoteConfigConfiguration getRemoteConfigConfiguration() {
return remoteConfig;
}
public S3ObjectMonitorFactory getDynamicConfig() {
return dynamicConfig;
}
public BadgesConfiguration getBadges() {
return badges;
}
public SubscriptionConfiguration getSubscription() {
return subscription;
}
public OneTimeDonationConfiguration getOneTimeDonations() {
return oneTimeDonations;
}
public PagedSingleUseKEMPreKeyStoreConfiguration getPagedSingleUseKEMPreKeyStore() {
return pagedSingleUseKEMPreKeyStore;
}
public ReportMessageConfiguration getReportMessageConfiguration() {
return reportMessage;
}
public SpamFilterConfiguration getSpamFilterConfiguration() {
return spamFilter;
}
public RegistrationServiceClientFactory getRegistrationServiceConfiguration() {
return registrationService;
}
public TurnConfiguration getTurnConfiguration() {
return turn;
}
public TusConfiguration getTus() {
return tus;
}
public ClientReleaseConfiguration getClientReleaseConfiguration() {
return clientRelease;
}
public MessageByteLimitCardinalityEstimatorConfiguration getMessageByteLimitCardinalityEstimator() {
return messageByteLimitCardinalityEstimator;
}
public LinkDeviceSecretConfiguration getLinkDeviceSecretConfiguration() {
return linkDevice;
}
public VirtualThreadConfiguration getVirtualThreadConfiguration() {
return virtualThread;
}
public ExternalRequestFilterConfiguration getExternalRequestFilterConfiguration() {
return externalRequestFilter;
}
public KeyTransparencyServiceConfiguration getKeyTransparencyServiceConfiguration() {
return keyTransparencyService;
}
public boolean logMessageDeliveryLoops() {
return logMessageDeliveryLoops;
}
public IdlePrimaryDeviceReminderConfiguration idlePrimaryDeviceReminderConfiguration() {
return idlePrimaryDeviceReminder;
}
public Map<String, CircuitBreakerConfiguration> getCircuitBreakerConfigurations() {
return circuitBreakers;
}
public Map<String, RetryConfiguration> getRetryConfigurations() {
return retries;
}
public RetryConfiguration getGeneralRedisRetryConfiguration() {
return generalRedisRetry;
}
public GrpcConfiguration getGrpc() {
return grpc;
}
public S3ObjectMonitorFactory getAsnTableConfiguration() {
return asnTable;
}
public CallQualitySurveyConfiguration getCallQualitySurveyConfiguration() {
return callQualitySurvey;
}
public HlrLookupConfiguration getHlrLookupConfiguration() {
return hlrLookup;
}
public ChangeNumberConfiguration getChangeNumber() {
return changeNumber;
}
public FoundationDbMessagesConfiguration getFoundationDbMessagesConfiguration() {
return foundationDbMessages;
}
}

View File

@ -1,18 +0,0 @@
/*
* Copyright 2013-2021 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.asn;
import static java.util.Objects.requireNonNull;
import com.google.i18n.phonenumbers.PhoneNumberUtil;
import javax.annotation.Nonnull;
public record AsnInfo(long asn, @Nonnull String regionCode) {
public AsnInfo {
requireNonNull(regionCode, "regionCode must not be null");
}
}

View File

@ -1,21 +0,0 @@
/*
* Copyright 2013-2021 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.asn;
import java.util.Optional;
import javax.annotation.Nonnull;
public interface AsnInfoProvider {
/// Gets ASN information for an IP address.
///
/// @param ipString a string representation of an IP address
///
/// @return ASN information for the given IP address or empty if no ASN information was found for the given IP address
Optional<AsnInfo> lookup(@Nonnull String ipString);
AsnInfoProvider EMPTY = _ -> Optional.empty();
}

View File

@ -1,165 +0,0 @@
/*
* Copyright 2013-2021 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.asn;
import static java.util.Objects.requireNonNull;
import com.google.common.annotations.VisibleForTesting;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.invoke.MethodHandles;
import java.math.BigInteger;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.Map;
import java.util.NavigableMap;
import java.util.Optional;
import java.util.TreeMap;
import java.util.zip.GZIPInputStream;
import javax.annotation.Nonnull;
import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVParser;
import org.apache.commons.csv.CSVRecord;
import org.apache.commons.lang3.Validate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* {@code AsnInfoProvider} implementation that supports both IPv4 and IPv6.
*/
public class AsnInfoProviderImpl implements AsnInfoProvider {
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
@Nonnull
private final NavigableMap<Long, AsnRange<Long>> asnBlocksByFirstIpv4;
@Nonnull
private final NavigableMap<BigInteger, AsnRange<BigInteger>> asnBlocksByFirstIpv6;
/**
* Creates an instance of {@code AsnInfoProviderImpl} using data from <a href="https://iptoasn.com/">iptoasn.com</a>.
* @param tsvGzInputStream gzip input stream representing the data.
*/
@Nonnull
public static AsnInfoProviderImpl fromTsvGz(@Nonnull final InputStream tsvGzInputStream) {
try (final GZIPInputStream inputStream = new GZIPInputStream(tsvGzInputStream)) {
return fromTsv(inputStream);
} catch (final IOException e) {
log.error("failed to ungzip the input stream", e);
throw new RuntimeException(e);
}
}
/**
* Creates an instance of {@code AsnInfoProviderImpl} using data from <a href="https://iptoasn.com/">iptoasn.com</a>.
* @param tsvInputStream input stream representing the data.
*/
@Nonnull
public static AsnInfoProviderImpl fromTsv(@Nonnull final InputStream tsvInputStream) {
try (final InputStreamReader tsvReader = new InputStreamReader(tsvInputStream)) {
final NavigableMap<Long, AsnRange<Long>> ip4asns = new TreeMap<>();
final NavigableMap<BigInteger, AsnRange<BigInteger>> ip6asns = new TreeMap<>();
final Map<Long, AsnInfo> asnInfoCache = new HashMap<>();
try (final CSVParser csvParser = CSVFormat.TDF.parse(tsvReader)) {
for (final CSVRecord record : csvParser) {
// format:
// range_start_ip_string range_end_ip_string AS_number country_code AS_description
final InetAddress startIp = InetAddress.getByName(record.get(0));
final InetAddress endIp = InetAddress.getByName(record.get(1));
final long asn = Long.parseLong(record.get(2));
final String regionCode = record.get(3);
// country code should be the same for any ASN, so we're caching AsnInfo objects
// not to have multiple instances with the same values
final AsnInfo asnInfo = asnInfoCache.computeIfAbsent(asn, k -> new AsnInfo(asn, regionCode));
if (!regionCode.equals(asnInfo.regionCode())) {
log.warn("ASN {} mapped to country codes {} and {}", asn, regionCode, asnInfo.regionCode());
}
// IPv4
if (startIp instanceof Inet4Address) {
final AsnRange<Long> asnRange = new AsnRange<>(
ip4BytesToLong((Inet4Address) startIp),
ip4BytesToLong((Inet4Address) endIp),
asnInfo
);
ip4asns.put(asnRange.from(), asnRange);
}
// IPv6
if (startIp instanceof Inet6Address) {
final AsnRange<BigInteger> asnRange = new AsnRange<>(
ip6BytesToBigInteger((Inet6Address) startIp),
ip6BytesToBigInteger((Inet6Address) endIp),
asnInfo
);
ip6asns.put(asnRange.from(), asnRange);
}
}
}
return new AsnInfoProviderImpl(ip4asns, ip6asns);
} catch (final Exception e) {
throw new RuntimeException(e);
}
}
public AsnInfoProviderImpl(
@Nonnull final NavigableMap<Long, AsnRange<Long>> asnBlocksByFirstIpv4,
@Nonnull final NavigableMap<BigInteger, AsnRange<BigInteger>> asnBlocksByFirstIpv6) {
this.asnBlocksByFirstIpv4 = requireNonNull(asnBlocksByFirstIpv4);
this.asnBlocksByFirstIpv6 = requireNonNull(asnBlocksByFirstIpv6);
}
@Nonnull
@Override
public Optional<AsnInfo> lookup(@Nonnull final String ipString) {
try {
final InetAddress address = InetAddress.getByName(ipString);
if (address instanceof Inet4Address ip4) {
final Long key = ip4BytesToLong(ip4);
return lookupInMap(asnBlocksByFirstIpv4, key);
}
if (address instanceof Inet6Address ip6) {
final BigInteger key = ip6BytesToBigInteger(ip6);
return lookupInMap(asnBlocksByFirstIpv6, key);
}
// safety net, should never happen
log.warn("Unknown InetAddress implementation: {}", address.getClass().getName());
} catch (final Exception e) {
log.error("Could not resolve ASN for IP string {}", ipString);
}
return Optional.empty();
}
@VisibleForTesting
protected static long ip4BytesToLong(@Nonnull final Inet4Address address) {
final byte[] arr = address.getAddress();
Validate.isTrue(arr.length == 4);
return Integer.toUnsignedLong(ByteBuffer.wrap(arr).getInt());
}
@VisibleForTesting
protected static BigInteger ip6BytesToBigInteger(@Nonnull final Inet6Address address) {
final byte[] arr = address.getAddress();
Validate.isTrue(arr.length == 16);
return new BigInteger(1, arr);
}
@Nonnull
private static <T extends Comparable<T>> Optional<AsnInfo> lookupInMap(
@Nonnull final NavigableMap<T, AsnRange<T>> map,
@Nonnull final T key) {
return Optional.ofNullable(map.floorEntry(key))
.filter(e -> e.getValue().contains(key) && e.getValue().asnInfo().asn() != 0)
.map(e -> e.getValue().asnInfo());
}
}

View File

@ -1,28 +0,0 @@
/*
* Copyright 2013-2021 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.asn;
import static java.util.Objects.requireNonNull;
import javax.annotation.Nonnull;
import org.apache.commons.lang3.Validate;
public record AsnRange<T extends Comparable<T>>(@Nonnull T from,
@Nonnull T to,
@Nonnull AsnInfo asnInfo) {
public AsnRange {
requireNonNull(from);
requireNonNull(to);
requireNonNull(asnInfo);
Validate.isTrue(from.compareTo(to) <= 0);
}
boolean contains(@Nonnull final T element) {
requireNonNull(element);
return from.compareTo(element) <= 0
&& element.compareTo(to) <= 0;
}
}

View File

@ -1,15 +0,0 @@
/*
* Copyright 2023 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.attachments;
import java.util.Map;
public interface AttachmentGenerator {
record Descriptor(Map<String, String> headers, String signedUploadLocation) {}
Descriptor generateAttachment(final String key, final long uploadLength);
}

View File

@ -1,20 +0,0 @@
/*
* Copyright 2026 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.attachments;
import java.security.SecureRandom;
import java.util.Base64;
public class AttachmentUtil {
public static final String CDN3_EXPERIMENT_NAME = "cdn3";
private AttachmentUtil() {}
public static String generateAttachmentKey(final SecureRandom secureRandom) {
final byte[] bytes = new byte[15];
secureRandom.nextBytes(bytes);
return Base64.getUrlEncoder().encodeToString(bytes);
}
}

View File

@ -1,53 +0,0 @@
/*
* Copyright 2023 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.attachments;
import org.whispersystems.textsecuregcm.gcp.CanonicalRequest;
import org.whispersystems.textsecuregcm.gcp.CanonicalRequestGenerator;
import org.whispersystems.textsecuregcm.gcp.CanonicalRequestSigner;
import javax.annotation.Nonnull;
import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.spec.InvalidKeySpecException;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.util.Map;
public class GcsAttachmentGenerator implements AttachmentGenerator {
@Nonnull
private final CanonicalRequestGenerator canonicalRequestGenerator;
@Nonnull
private final CanonicalRequestSigner canonicalRequestSigner;
public GcsAttachmentGenerator(@Nonnull String domain, @Nonnull String email,
@Nonnull String pathPrefix, @Nonnull String rsaSigningKey)
throws IOException, InvalidKeyException, InvalidKeySpecException {
this.canonicalRequestGenerator = new CanonicalRequestGenerator(domain, email, pathPrefix);
this.canonicalRequestSigner = new CanonicalRequestSigner(rsaSigningKey);
}
@Override
public Descriptor generateAttachment(final String key, final long uploadLength) {
final ZonedDateTime now = ZonedDateTime.now(ZoneOffset.UTC);
final CanonicalRequest canonicalRequest = canonicalRequestGenerator.createFor(key, now, uploadLength);
return new Descriptor(getHeaderMap(canonicalRequest), getSignedUploadLocation(canonicalRequest));
}
private String getSignedUploadLocation(@Nonnull CanonicalRequest canonicalRequest) {
return "https://" + canonicalRequest.getDomain() + canonicalRequest.getResourcePath()
+ '?' + canonicalRequest.getCanonicalQuery()
+ "&X-Goog-Signature=" + canonicalRequestSigner.sign(canonicalRequest);
}
private static Map<String, String> getHeaderMap(@Nonnull CanonicalRequest canonicalRequest) {
return Map.of(
"host", canonicalRequest.getDomain(),
"x-goog-content-length-range", "1," + canonicalRequest.getMaxSizeInBytes(),
"x-goog-resumable", "start");
}
}

View File

@ -1,40 +0,0 @@
/*
* Copyright 2023 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.attachments;
import org.apache.http.HttpHeaders;
import org.whispersystems.textsecuregcm.auth.JwtGenerator;
import org.whispersystems.textsecuregcm.util.HeaderUtils;
import java.nio.charset.StandardCharsets;
import java.time.Clock;
import java.util.Base64;
import java.util.Map;
public class TusAttachmentGenerator implements AttachmentGenerator {
private static final String ATTACHMENTS = "attachments";
private final JwtGenerator jwtGenerator;
private final String tusUri;
public TusAttachmentGenerator(final TusConfiguration cfg) {
this.tusUri = cfg.uploadUri();
this.jwtGenerator = new JwtGenerator(cfg.userAuthenticationTokenSharedSecret().value(), Clock.systemUTC());
}
@Override
public Descriptor generateAttachment(final String key, final long uploadLength) {
final String token = jwtGenerator.generateJwt(ATTACHMENTS, key,
builder -> builder.withClaim(JwtGenerator.MAX_LENGTH_CLAIM_KEY, uploadLength));
final String b64Key = Base64.getEncoder().encodeToString(key.getBytes(StandardCharsets.UTF_8));
final Map<String, String> headers = Map.of(
HttpHeaders.AUTHORIZATION, HeaderUtils.bearerAuthHeader(token),
"Upload-Metadata", String.format("filename %s", b64Key)
);
return new Descriptor(headers, tusUri + "/" + ATTACHMENTS);
}
}

View File

@ -1,15 +0,0 @@
/*
* Copyright 2023 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.attachments;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.Positive;
import org.whispersystems.textsecuregcm.configuration.secrets.SecretBytes;
import org.whispersystems.textsecuregcm.util.ExactlySize;
public record TusConfiguration(
@ExactlySize(32) SecretBytes userAuthenticationTokenSharedSecret,
@NotEmpty String uploadUri){}

View File

@ -1,150 +0,0 @@
/*
* Copyright 2013 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 io.dropwizard.auth.Authenticator;
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.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Optional;
import java.util.UUID;
import org.apache.commons.lang3.StringUtils;
import org.whispersystems.textsecuregcm.identity.IdentityType;
import org.whispersystems.textsecuregcm.storage.Account;
import org.whispersystems.textsecuregcm.storage.AccountsManager;
import org.whispersystems.textsecuregcm.storage.Device;
import org.whispersystems.textsecuregcm.util.Pair;
import org.whispersystems.textsecuregcm.util.Util;
public class AccountAuthenticator implements Authenticator<BasicCredentials, AuthenticatedDevice> {
private static final String AUTHENTICATION_COUNTER_NAME = name(AccountAuthenticator.class, "authentication");
private static final String AUTHENTICATION_SUCCEEDED_TAG_NAME = "succeeded";
private static final String AUTHENTICATION_FAILURE_REASON_TAG_NAME = "reason";
private static final String DAYS_SINCE_LAST_SEEN_DISTRIBUTION_NAME = name(AccountAuthenticator.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 AccountAuthenticator(AccountsManager accountsManager) {
this(accountsManager, Clock.systemUTC());
}
@VisibleForTesting
public AccountAuthenticator(AccountsManager accountsManager, Clock clock) {
this.accountsManager = accountsManager;
this.clock = clock;
}
static Pair<String, Byte> getIdentifierAndDeviceId(final String basicUsername) {
final String identifier;
final byte deviceId;
final int deviceIdSeparatorIndex = basicUsername.indexOf(DEVICE_ID_SEPARATOR);
if (deviceIdSeparatorIndex == -1) {
identifier = basicUsername;
deviceId = Device.PRIMARY_ID;
} else {
identifier = basicUsername.substring(0, deviceIdSeparatorIndex);
deviceId = Byte.parseByte(basicUsername.substring(deviceIdSeparatorIndex + 1));
}
return new Pair<>(identifier, deviceId);
}
@Override
public Optional<AuthenticatedDevice> authenticate(BasicCredentials basicCredentials) {
boolean succeeded = false;
String failureReason = null;
try {
final UUID accountUuid;
final byte deviceId;
{
final Pair<String, Byte> 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();
}
Optional<Device> device = account.get().getDevice(deviceId);
if (device.isEmpty()) {
failureReason = "noSuchDevice";
return Optional.empty();
}
SaltedTokenHash deviceSaltedTokenHash = device.get().getAuthTokenHash();
if (deviceSaltedTokenHash.verify(basicCredentials.getPassword())) {
succeeded = true;
final Account authenticatedAccount = updateLastSeen(account.get(), device.get());
return Optional.of(new AuthenticatedDevice(authenticatedAccount.getIdentifier(IdentityType.ACI),
device.get().getId(),
Instant.ofEpochMilli(authenticatedAccount.getPrimaryDevice().getLastSeen())));
} else {
failureReason = "incorrectPassword";
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();
}
}
@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 ensures 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.isPrimary()))
.record(Duration.ofMillis(todayInMillisWithOffset - device.getLastSeen()).toDays());
return accountsManager.updateDeviceLastSeen(account.getIdentifier(IdentityType.ACI), device, Util.todayInMillis(clock));
}
return account;
}
}

View File

@ -1,30 +0,0 @@
/*
* Copyright 2013-2020 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.auth;
import jakarta.ws.rs.WebApplicationException;
import jakarta.ws.rs.core.Response;
import java.util.Base64;
public class Anonymous {
private final byte[] unidentifiedSenderAccessKey;
public Anonymous(String header) {
try {
this.unidentifiedSenderAccessKey = Base64.getDecoder().decode(header);
if (unidentifiedSenderAccessKey.length != UnidentifiedAccessUtil.UNIDENTIFIED_ACCESS_KEY_LENGTH) {
throw new WebApplicationException("access key length must be 16", Response.Status.UNAUTHORIZED);
}
} catch (IllegalArgumentException e) {
throw new WebApplicationException(e, Response.Status.UNAUTHORIZED);
}
}
public byte[] getAccessKey() {
return unidentifiedSenderAccessKey;
}
}

View File

@ -1,20 +0,0 @@
/*
* Copyright 2023 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.auth;
import org.signal.libsignal.zkgroup.backups.BackupCredentialType;
import org.signal.libsignal.zkgroup.backups.BackupLevel;
import org.whispersystems.textsecuregcm.util.ua.UserAgent;
import javax.annotation.Nullable;
public record AuthenticatedBackupUser(
byte[] backupId,
BackupCredentialType credentialType,
BackupLevel backupLevel,
String backupDir,
String mediaDir,
@Nullable UserAgent userAgent) {
}

View File

@ -1,25 +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.time.Instant;
import java.util.UUID;
import javax.security.auth.Subject;
public record AuthenticatedDevice(UUID accountIdentifier, byte deviceId, Instant primaryDeviceLastSeen)
implements Principal {
@Override
public String getName() {
return null;
}
@Override
public boolean implies(final Subject subject) {
return false;
}
}

View File

@ -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 byte deviceId;
private final String password;
private BasicAuthorizationHeader(final String username, final byte 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 byte deviceId;
{
final Pair<String, Byte> identifierAndDeviceId =
AccountAuthenticator.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;
}
}

View File

@ -1,65 +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.util.concurrent.TimeUnit;
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.identity.IdentityType;
import org.whispersystems.textsecuregcm.storage.Account;
import org.whispersystems.textsecuregcm.util.UUIDUtil;
public class CertificateGenerator {
private final ECPrivateKey privateKey;
private final int expiresDays;
private final boolean embedSigner;
private final ServerCertificate serverCertificate;
private final int serverCertificateId;
public CertificateGenerator(byte[] serverCertificate, ECPrivateKey privateKey, int expiresDays, boolean embedSigner)
throws InvalidProtocolBufferException {
this.privateKey = privateKey;
this.expiresDays = expiresDays;
this.embedSigner = embedSigner;
this.serverCertificate = ServerCertificate.parseFrom(serverCertificate);
this.serverCertificateId = ServerCertificate.Certificate
.parseFrom(this.serverCertificate.getCertificate())
.getId();
}
public byte[] createFor(final Account account, final byte deviceId, boolean includeE164) {
SenderCertificate.Certificate.Builder builder = SenderCertificate.Certificate.newBuilder()
.setSenderDevice(Math.toIntExact(deviceId))
.setExpires(System.currentTimeMillis() + TimeUnit.DAYS.toMillis(expiresDays))
.setIdentityKey(ByteString.copyFrom(account.getIdentityKey(IdentityType.ACI).serialize()))
.setSenderUuid(UUIDUtil.toByteString(account.getUuid()));
if (includeE164) {
builder.setSenderE164(account.getNumber());
}
if (embedSigner) {
builder.setSignerCertificate(serverCertificate);
} else {
builder.setSignerId(serverCertificateId);
}
byte[] certificate = builder.build().toByteArray();
byte[] signature;
signature = privateKey.calculateSignature(certificate);
return SenderCertificate.newBuilder()
.setCertificate(ByteString.copyFrom(certificate))
.setSignature(ByteString.copyFrom(signature))
.build()
.toByteArray();
}
}

View File

@ -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 ChangesLinkedDevices {
}

View File

@ -1,20 +0,0 @@
/*
* Copyright 2024 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 changes the phone number and PNI keys 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 ChangesPhoneNumber {
}

Some files were not shown because too many files have changed in this diff Show More