matrix-synapse 1.145.0rc2__cp310-abi3-manylinux_2_28_x86_64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of matrix-synapse might be problematic. Click here for more details.

Files changed (1636) hide show
  1. AUTHORS.rst +51 -0
  2. CHANGES.md +1732 -0
  3. CONTRIBUTING.md +3 -0
  4. Cargo.lock +1923 -0
  5. Cargo.toml +6 -0
  6. INSTALL.md +7 -0
  7. LICENSE-AGPL-3.0 +661 -0
  8. LICENSE-COMMERCIAL +6 -0
  9. UPGRADE.rst +7 -0
  10. book.toml +47 -0
  11. changelog.d/.gitignore +1 -0
  12. demo/.gitignore +4 -0
  13. demo/clean.sh +22 -0
  14. demo/start.sh +173 -0
  15. demo/stop.sh +14 -0
  16. docs/.sample_config_header.yaml +12 -0
  17. docs/CAPTCHA_SETUP.md +37 -0
  18. docs/README.md +86 -0
  19. docs/SUMMARY.md +126 -0
  20. docs/admin_api/README.rst +13 -0
  21. docs/admin_api/account_validity.md +47 -0
  22. docs/admin_api/client_server_api_extensions.md +67 -0
  23. docs/admin_api/event_reports.md +185 -0
  24. docs/admin_api/experimental_features.md +52 -0
  25. docs/admin_api/fetch_event.md +53 -0
  26. docs/admin_api/media_admin_api.md +372 -0
  27. docs/admin_api/purge_history_api.md +77 -0
  28. docs/admin_api/register_api.md +92 -0
  29. docs/admin_api/room_membership.md +35 -0
  30. docs/admin_api/rooms.md +1190 -0
  31. docs/admin_api/scheduled_tasks.md +54 -0
  32. docs/admin_api/server_notices.md +48 -0
  33. docs/admin_api/statistics.md +132 -0
  34. docs/admin_api/user_admin_api.md +1602 -0
  35. docs/admin_api/version_api.md +23 -0
  36. docs/ancient_architecture_notes.md +81 -0
  37. docs/application_services.md +36 -0
  38. docs/architecture.md +65 -0
  39. docs/auth_chain_diff.dot +32 -0
  40. docs/auth_chain_diff.dot.png +0 -0
  41. docs/auth_chain_difference_algorithm.md +141 -0
  42. docs/changelogs/CHANGES-2019.md +1039 -0
  43. docs/changelogs/CHANGES-2020.md +2145 -0
  44. docs/changelogs/CHANGES-2021.md +2573 -0
  45. docs/changelogs/CHANGES-2022.md +2766 -0
  46. docs/changelogs/CHANGES-2023.md +2202 -0
  47. docs/changelogs/CHANGES-2024.md +1586 -0
  48. docs/changelogs/CHANGES-pre-1.0.md +3641 -0
  49. docs/changelogs/README.md +1 -0
  50. docs/code_style.md +119 -0
  51. docs/consent_tracking.md +197 -0
  52. docs/delegate.md +111 -0
  53. docs/deprecation_policy.md +89 -0
  54. docs/development/cas.md +64 -0
  55. docs/development/contributing_guide.md +554 -0
  56. docs/development/database_schema.md +404 -0
  57. docs/development/demo.md +42 -0
  58. docs/development/dependencies.md +326 -0
  59. docs/development/experimental_features.md +37 -0
  60. docs/development/git.md +148 -0
  61. docs/development/img/git/branches.jpg +0 -0
  62. docs/development/img/git/clean.png +0 -0
  63. docs/development/img/git/squash.png +0 -0
  64. docs/development/internal_documentation/README.md +12 -0
  65. docs/development/internal_documentation/release_notes_review_checklist.md +12 -0
  66. docs/development/releases.md +37 -0
  67. docs/development/reviews.md +41 -0
  68. docs/development/room-dag-concepts.md +115 -0
  69. docs/development/saml.md +40 -0
  70. docs/development/synapse_architecture/cancellation.md +392 -0
  71. docs/development/synapse_architecture/faster_joins.md +375 -0
  72. docs/development/synapse_architecture/streams.md +198 -0
  73. docs/element_logo_white_bg.svg +94 -0
  74. docs/favicon.png +0 -0
  75. docs/favicon.svg +58 -0
  76. docs/federate.md +67 -0
  77. docs/jwt.md +105 -0
  78. docs/log_contexts.md +566 -0
  79. docs/manhole.md +101 -0
  80. docs/media_repository.md +78 -0
  81. docs/message_retention_policies.md +207 -0
  82. docs/metrics-howto.md +315 -0
  83. docs/modules/account_data_callbacks.md +106 -0
  84. docs/modules/account_validity_callbacks.md +57 -0
  85. docs/modules/add_extra_fields_to_client_events_unsigned.md +32 -0
  86. docs/modules/background_update_controller_callbacks.md +71 -0
  87. docs/modules/index.md +53 -0
  88. docs/modules/media_repository_callbacks.md +131 -0
  89. docs/modules/password_auth_provider_callbacks.md +270 -0
  90. docs/modules/porting_legacy_module.md +20 -0
  91. docs/modules/presence_router_callbacks.md +112 -0
  92. docs/modules/ratelimit_callbacks.md +43 -0
  93. docs/modules/spam_checker_callbacks.md +517 -0
  94. docs/modules/third_party_rules_callbacks.md +350 -0
  95. docs/modules/writing_a_module.md +141 -0
  96. docs/openid.md +783 -0
  97. docs/opentracing.md +99 -0
  98. docs/other/running_synapse_on_single_board_computers.md +75 -0
  99. docs/password_auth_providers.md +129 -0
  100. docs/postgres.md +269 -0
  101. docs/presence_router_module.md +247 -0
  102. docs/privacy_policy_templates/en/1.0.html +26 -0
  103. docs/privacy_policy_templates/en/success.html +11 -0
  104. docs/replication.md +36 -0
  105. docs/reverse_proxy.md +327 -0
  106. docs/room_and_user_statistics.md +22 -0
  107. docs/sample_config.yaml +47 -0
  108. docs/sample_log_config.yaml +75 -0
  109. docs/server_notices.md +66 -0
  110. docs/setup/forward_proxy.md +89 -0
  111. docs/setup/installation.md +656 -0
  112. docs/setup/security.md +41 -0
  113. docs/setup/turn/coturn.md +197 -0
  114. docs/setup/turn/eturnal.md +170 -0
  115. docs/spam_checker.md +122 -0
  116. docs/sso_mapping_providers.md +208 -0
  117. docs/structured_logging.md +80 -0
  118. docs/synctl_workers.md +36 -0
  119. docs/systemd-with-workers/README.md +101 -0
  120. docs/systemd-with-workers/system/matrix-synapse-worker@.service +26 -0
  121. docs/systemd-with-workers/system/matrix-synapse.service +23 -0
  122. docs/systemd-with-workers/system/matrix-synapse.target +6 -0
  123. docs/systemd-with-workers/workers/background_worker.yaml +4 -0
  124. docs/systemd-with-workers/workers/event_persister.yaml +20 -0
  125. docs/systemd-with-workers/workers/federation_sender.yaml +4 -0
  126. docs/systemd-with-workers/workers/generic_worker.yaml +11 -0
  127. docs/systemd-with-workers/workers/media_worker.yaml +11 -0
  128. docs/systemd-with-workers/workers/pusher_worker.yaml +4 -0
  129. docs/tcp_replication.md +258 -0
  130. docs/templates.md +254 -0
  131. docs/turn-howto.md +168 -0
  132. docs/upgrade.md +2876 -0
  133. docs/usage/administration/README.md +7 -0
  134. docs/usage/administration/admin_api/README.md +47 -0
  135. docs/usage/administration/admin_api/background_updates.md +109 -0
  136. docs/usage/administration/admin_api/federation.md +212 -0
  137. docs/usage/administration/admin_api/registration_tokens.md +298 -0
  138. docs/usage/administration/admin_faq.md +294 -0
  139. docs/usage/administration/backups.md +125 -0
  140. docs/usage/administration/database_maintenance_tools.md +18 -0
  141. docs/usage/administration/monitoring/reporting_homeserver_usage_statistics.md +77 -0
  142. docs/usage/administration/monthly_active_users.md +84 -0
  143. docs/usage/administration/request_log.md +44 -0
  144. docs/usage/administration/state_groups.md +25 -0
  145. docs/usage/administration/understanding_synapse_through_grafana_graphs.md +83 -0
  146. docs/usage/administration/useful_sql_for_admins.md +216 -0
  147. docs/usage/configuration/README.md +4 -0
  148. docs/usage/configuration/config_documentation.md +4768 -0
  149. docs/usage/configuration/homeserver_sample_config.md +17 -0
  150. docs/usage/configuration/logging_sample_config.md +19 -0
  151. docs/usage/configuration/user_authentication/README.md +15 -0
  152. docs/usage/configuration/user_authentication/refresh_tokens.md +139 -0
  153. docs/usage/configuration/user_authentication/single_sign_on/README.md +5 -0
  154. docs/usage/configuration/user_authentication/single_sign_on/cas.md +8 -0
  155. docs/usage/configuration/user_authentication/single_sign_on/saml.md +8 -0
  156. docs/user_directory.md +130 -0
  157. docs/website_files/README.md +35 -0
  158. docs/website_files/indent-section-headers.css +7 -0
  159. docs/website_files/remove-nav-buttons.css +8 -0
  160. docs/website_files/table-of-contents.css +47 -0
  161. docs/website_files/table-of-contents.js +148 -0
  162. docs/website_files/theme/index.hbs +324 -0
  163. docs/website_files/version-picker.css +78 -0
  164. docs/website_files/version-picker.js +147 -0
  165. docs/website_files/version.js +1 -0
  166. docs/welcome_and_overview.md +67 -0
  167. docs/workers.md +897 -0
  168. matrix_synapse-1.145.0rc2.dist-info/METADATA +260 -0
  169. matrix_synapse-1.145.0rc2.dist-info/RECORD +1636 -0
  170. matrix_synapse-1.145.0rc2.dist-info/WHEEL +5 -0
  171. matrix_synapse-1.145.0rc2.dist-info/entry_points.txt +13 -0
  172. matrix_synapse-1.145.0rc2.dist-info/licenses/AUTHORS.rst +51 -0
  173. matrix_synapse-1.145.0rc2.dist-info/licenses/LICENSE-AGPL-3.0 +661 -0
  174. matrix_synapse-1.145.0rc2.dist-info/licenses/LICENSE-COMMERCIAL +6 -0
  175. mypy.ini +115 -0
  176. rust/Cargo.toml +63 -0
  177. rust/build.rs +45 -0
  178. scripts-dev/build_debian_packages.py +228 -0
  179. scripts-dev/check-newsfragment.sh +62 -0
  180. scripts-dev/check_line_terminators.sh +29 -0
  181. scripts-dev/check_locked_deps_have_sdists.py +64 -0
  182. scripts-dev/check_schema_delta.py +240 -0
  183. scripts-dev/complement.sh +332 -0
  184. scripts-dev/config-lint.sh +15 -0
  185. scripts-dev/database-save.sh +15 -0
  186. scripts-dev/docker_update_debian_changelog.sh +70 -0
  187. scripts-dev/dump_macaroon.py +25 -0
  188. scripts-dev/federation_client.py +428 -0
  189. scripts-dev/gen_config_documentation.py +510 -0
  190. scripts-dev/generate_sample_config.sh +28 -0
  191. scripts-dev/lint.sh +141 -0
  192. scripts-dev/make_full_schema.sh +310 -0
  193. scripts-dev/mypy_synapse_plugin.py +918 -0
  194. scripts-dev/next_github_number.sh +9 -0
  195. scripts-dev/release.py +986 -0
  196. scripts-dev/schema_versions.py +182 -0
  197. scripts-dev/sign_json.py +172 -0
  198. synapse/__init__.py +97 -0
  199. synapse/_scripts/__init__.py +0 -0
  200. synapse/_scripts/export_signing_key.py +109 -0
  201. synapse/_scripts/generate_config.py +83 -0
  202. synapse/_scripts/generate_log_config.py +56 -0
  203. synapse/_scripts/generate_signing_key.py +55 -0
  204. synapse/_scripts/generate_workers_map.py +318 -0
  205. synapse/_scripts/hash_password.py +95 -0
  206. synapse/_scripts/move_remote_media_to_new_store.py +128 -0
  207. synapse/_scripts/register_new_matrix_user.py +402 -0
  208. synapse/_scripts/review_recent_signups.py +212 -0
  209. synapse/_scripts/synapse_port_db.py +1604 -0
  210. synapse/_scripts/synctl.py +365 -0
  211. synapse/_scripts/update_synapse_database.py +130 -0
  212. synapse/api/__init__.py +20 -0
  213. synapse/api/auth/__init__.py +207 -0
  214. synapse/api/auth/base.py +406 -0
  215. synapse/api/auth/internal.py +299 -0
  216. synapse/api/auth/mas.py +436 -0
  217. synapse/api/auth/msc3861_delegated.py +617 -0
  218. synapse/api/auth_blocking.py +144 -0
  219. synapse/api/constants.py +379 -0
  220. synapse/api/errors.py +913 -0
  221. synapse/api/filtering.py +537 -0
  222. synapse/api/presence.py +102 -0
  223. synapse/api/ratelimiting.py +481 -0
  224. synapse/api/room_versions.py +535 -0
  225. synapse/api/urls.py +118 -0
  226. synapse/app/__init__.py +62 -0
  227. synapse/app/_base.py +906 -0
  228. synapse/app/admin_cmd.py +388 -0
  229. synapse/app/appservice.py +30 -0
  230. synapse/app/client_reader.py +30 -0
  231. synapse/app/complement_fork_starter.py +206 -0
  232. synapse/app/event_creator.py +29 -0
  233. synapse/app/federation_reader.py +30 -0
  234. synapse/app/federation_sender.py +30 -0
  235. synapse/app/frontend_proxy.py +30 -0
  236. synapse/app/generic_worker.py +475 -0
  237. synapse/app/homeserver.py +505 -0
  238. synapse/app/media_repository.py +30 -0
  239. synapse/app/phone_stats_home.py +292 -0
  240. synapse/app/pusher.py +30 -0
  241. synapse/app/synchrotron.py +30 -0
  242. synapse/app/user_dir.py +31 -0
  243. synapse/appservice/__init__.py +458 -0
  244. synapse/appservice/api.py +567 -0
  245. synapse/appservice/scheduler.py +565 -0
  246. synapse/config/__init__.py +27 -0
  247. synapse/config/__main__.py +62 -0
  248. synapse/config/_base.py +1154 -0
  249. synapse/config/_base.pyi +216 -0
  250. synapse/config/_util.py +99 -0
  251. synapse/config/account_validity.py +116 -0
  252. synapse/config/api.py +141 -0
  253. synapse/config/appservice.py +210 -0
  254. synapse/config/auth.py +80 -0
  255. synapse/config/auto_accept_invites.py +43 -0
  256. synapse/config/background_updates.py +44 -0
  257. synapse/config/cache.py +231 -0
  258. synapse/config/captcha.py +90 -0
  259. synapse/config/cas.py +116 -0
  260. synapse/config/consent.py +73 -0
  261. synapse/config/database.py +184 -0
  262. synapse/config/emailconfig.py +367 -0
  263. synapse/config/experimental.py +601 -0
  264. synapse/config/federation.py +114 -0
  265. synapse/config/homeserver.py +141 -0
  266. synapse/config/jwt.py +55 -0
  267. synapse/config/key.py +447 -0
  268. synapse/config/logger.py +390 -0
  269. synapse/config/mas.py +192 -0
  270. synapse/config/matrixrtc.py +66 -0
  271. synapse/config/metrics.py +93 -0
  272. synapse/config/modules.py +40 -0
  273. synapse/config/oembed.py +185 -0
  274. synapse/config/oidc.py +509 -0
  275. synapse/config/password_auth_providers.py +82 -0
  276. synapse/config/push.py +64 -0
  277. synapse/config/ratelimiting.py +260 -0
  278. synapse/config/redis.py +74 -0
  279. synapse/config/registration.py +296 -0
  280. synapse/config/repository.py +311 -0
  281. synapse/config/retention.py +162 -0
  282. synapse/config/room.py +88 -0
  283. synapse/config/room_directory.py +165 -0
  284. synapse/config/saml2.py +251 -0
  285. synapse/config/server.py +1139 -0
  286. synapse/config/server_notices.py +84 -0
  287. synapse/config/spam_checker.py +66 -0
  288. synapse/config/sso.py +121 -0
  289. synapse/config/stats.py +54 -0
  290. synapse/config/third_party_event_rules.py +40 -0
  291. synapse/config/tls.py +192 -0
  292. synapse/config/tracer.py +71 -0
  293. synapse/config/user_directory.py +47 -0
  294. synapse/config/user_types.py +42 -0
  295. synapse/config/voip.py +59 -0
  296. synapse/config/workers.py +642 -0
  297. synapse/crypto/__init__.py +20 -0
  298. synapse/crypto/context_factory.py +278 -0
  299. synapse/crypto/event_signing.py +194 -0
  300. synapse/crypto/keyring.py +980 -0
  301. synapse/event_auth.py +1266 -0
  302. synapse/events/__init__.py +667 -0
  303. synapse/events/auto_accept_invites.py +216 -0
  304. synapse/events/builder.py +387 -0
  305. synapse/events/presence_router.py +243 -0
  306. synapse/events/snapshot.py +559 -0
  307. synapse/events/utils.py +924 -0
  308. synapse/events/validator.py +305 -0
  309. synapse/federation/__init__.py +22 -0
  310. synapse/federation/federation_base.py +382 -0
  311. synapse/federation/federation_client.py +2133 -0
  312. synapse/federation/federation_server.py +1543 -0
  313. synapse/federation/persistence.py +70 -0
  314. synapse/federation/send_queue.py +532 -0
  315. synapse/federation/sender/__init__.py +1165 -0
  316. synapse/federation/sender/per_destination_queue.py +886 -0
  317. synapse/federation/sender/transaction_manager.py +210 -0
  318. synapse/federation/transport/__init__.py +28 -0
  319. synapse/federation/transport/client.py +1199 -0
  320. synapse/federation/transport/server/__init__.py +334 -0
  321. synapse/federation/transport/server/_base.py +429 -0
  322. synapse/federation/transport/server/federation.py +910 -0
  323. synapse/federation/units.py +133 -0
  324. synapse/handlers/__init__.py +20 -0
  325. synapse/handlers/account.py +162 -0
  326. synapse/handlers/account_data.py +360 -0
  327. synapse/handlers/account_validity.py +362 -0
  328. synapse/handlers/admin.py +615 -0
  329. synapse/handlers/appservice.py +989 -0
  330. synapse/handlers/auth.py +2482 -0
  331. synapse/handlers/cas.py +413 -0
  332. synapse/handlers/deactivate_account.py +363 -0
  333. synapse/handlers/delayed_events.py +607 -0
  334. synapse/handlers/device.py +1869 -0
  335. synapse/handlers/devicemessage.py +399 -0
  336. synapse/handlers/directory.py +545 -0
  337. synapse/handlers/e2e_keys.py +1835 -0
  338. synapse/handlers/e2e_room_keys.py +455 -0
  339. synapse/handlers/event_auth.py +390 -0
  340. synapse/handlers/events.py +203 -0
  341. synapse/handlers/federation.py +2042 -0
  342. synapse/handlers/federation_event.py +2420 -0
  343. synapse/handlers/identity.py +812 -0
  344. synapse/handlers/initial_sync.py +528 -0
  345. synapse/handlers/jwt.py +120 -0
  346. synapse/handlers/message.py +2357 -0
  347. synapse/handlers/oidc.py +1801 -0
  348. synapse/handlers/pagination.py +811 -0
  349. synapse/handlers/password_policy.py +102 -0
  350. synapse/handlers/presence.py +2634 -0
  351. synapse/handlers/profile.py +656 -0
  352. synapse/handlers/push_rules.py +164 -0
  353. synapse/handlers/read_marker.py +79 -0
  354. synapse/handlers/receipts.py +351 -0
  355. synapse/handlers/register.py +1059 -0
  356. synapse/handlers/relations.py +623 -0
  357. synapse/handlers/reports.py +98 -0
  358. synapse/handlers/room.py +2449 -0
  359. synapse/handlers/room_list.py +632 -0
  360. synapse/handlers/room_member.py +2366 -0
  361. synapse/handlers/room_member_worker.py +146 -0
  362. synapse/handlers/room_policy.py +186 -0
  363. synapse/handlers/room_summary.py +1057 -0
  364. synapse/handlers/saml.py +524 -0
  365. synapse/handlers/search.py +723 -0
  366. synapse/handlers/send_email.py +209 -0
  367. synapse/handlers/set_password.py +71 -0
  368. synapse/handlers/sliding_sync/__init__.py +1961 -0
  369. synapse/handlers/sliding_sync/extensions.py +969 -0
  370. synapse/handlers/sliding_sync/room_lists.py +2317 -0
  371. synapse/handlers/sliding_sync/store.py +126 -0
  372. synapse/handlers/sso.py +1291 -0
  373. synapse/handlers/state_deltas.py +82 -0
  374. synapse/handlers/stats.py +322 -0
  375. synapse/handlers/sync.py +3096 -0
  376. synapse/handlers/thread_subscriptions.py +190 -0
  377. synapse/handlers/typing.py +612 -0
  378. synapse/handlers/ui_auth/__init__.py +48 -0
  379. synapse/handlers/ui_auth/checkers.py +332 -0
  380. synapse/handlers/user_directory.py +786 -0
  381. synapse/handlers/worker_lock.py +371 -0
  382. synapse/http/__init__.py +105 -0
  383. synapse/http/additional_resource.py +62 -0
  384. synapse/http/client.py +1377 -0
  385. synapse/http/connectproxyclient.py +316 -0
  386. synapse/http/federation/__init__.py +19 -0
  387. synapse/http/federation/matrix_federation_agent.py +490 -0
  388. synapse/http/federation/srv_resolver.py +196 -0
  389. synapse/http/federation/well_known_resolver.py +368 -0
  390. synapse/http/matrixfederationclient.py +1874 -0
  391. synapse/http/proxy.py +290 -0
  392. synapse/http/proxyagent.py +497 -0
  393. synapse/http/replicationagent.py +203 -0
  394. synapse/http/request_metrics.py +309 -0
  395. synapse/http/server.py +1111 -0
  396. synapse/http/servlet.py +1018 -0
  397. synapse/http/site.py +952 -0
  398. synapse/http/types.py +27 -0
  399. synapse/logging/__init__.py +31 -0
  400. synapse/logging/_remote.py +261 -0
  401. synapse/logging/_terse_json.py +95 -0
  402. synapse/logging/context.py +1209 -0
  403. synapse/logging/formatter.py +62 -0
  404. synapse/logging/handlers.py +99 -0
  405. synapse/logging/loggers.py +25 -0
  406. synapse/logging/opentracing.py +1132 -0
  407. synapse/logging/scopecontextmanager.py +160 -0
  408. synapse/media/_base.py +831 -0
  409. synapse/media/filepath.py +417 -0
  410. synapse/media/media_repository.py +1595 -0
  411. synapse/media/media_storage.py +703 -0
  412. synapse/media/oembed.py +277 -0
  413. synapse/media/preview_html.py +556 -0
  414. synapse/media/storage_provider.py +195 -0
  415. synapse/media/thumbnailer.py +833 -0
  416. synapse/media/url_previewer.py +884 -0
  417. synapse/metrics/__init__.py +748 -0
  418. synapse/metrics/_gc.py +219 -0
  419. synapse/metrics/_reactor_metrics.py +171 -0
  420. synapse/metrics/_types.py +38 -0
  421. synapse/metrics/background_process_metrics.py +555 -0
  422. synapse/metrics/common_usage_metrics.py +95 -0
  423. synapse/metrics/jemalloc.py +248 -0
  424. synapse/module_api/__init__.py +2131 -0
  425. synapse/module_api/callbacks/__init__.py +50 -0
  426. synapse/module_api/callbacks/account_validity_callbacks.py +106 -0
  427. synapse/module_api/callbacks/media_repository_callbacks.py +157 -0
  428. synapse/module_api/callbacks/ratelimit_callbacks.py +78 -0
  429. synapse/module_api/callbacks/spamchecker_callbacks.py +991 -0
  430. synapse/module_api/callbacks/third_party_event_rules_callbacks.py +592 -0
  431. synapse/module_api/errors.py +42 -0
  432. synapse/notifier.py +969 -0
  433. synapse/push/__init__.py +212 -0
  434. synapse/push/bulk_push_rule_evaluator.py +635 -0
  435. synapse/push/clientformat.py +126 -0
  436. synapse/push/emailpusher.py +334 -0
  437. synapse/push/httppusher.py +565 -0
  438. synapse/push/mailer.py +1009 -0
  439. synapse/push/presentable_names.py +216 -0
  440. synapse/push/push_tools.py +114 -0
  441. synapse/push/push_types.py +141 -0
  442. synapse/push/pusher.py +87 -0
  443. synapse/push/pusherpool.py +501 -0
  444. synapse/push/rulekinds.py +33 -0
  445. synapse/py.typed +0 -0
  446. synapse/replication/__init__.py +20 -0
  447. synapse/replication/http/__init__.py +68 -0
  448. synapse/replication/http/_base.py +469 -0
  449. synapse/replication/http/account_data.py +297 -0
  450. synapse/replication/http/deactivate_account.py +81 -0
  451. synapse/replication/http/delayed_events.py +62 -0
  452. synapse/replication/http/devices.py +254 -0
  453. synapse/replication/http/federation.py +334 -0
  454. synapse/replication/http/login.py +106 -0
  455. synapse/replication/http/membership.py +364 -0
  456. synapse/replication/http/presence.py +133 -0
  457. synapse/replication/http/push.py +156 -0
  458. synapse/replication/http/register.py +172 -0
  459. synapse/replication/http/send_events.py +182 -0
  460. synapse/replication/http/state.py +82 -0
  461. synapse/replication/http/streams.py +101 -0
  462. synapse/replication/tcp/__init__.py +56 -0
  463. synapse/replication/tcp/client.py +553 -0
  464. synapse/replication/tcp/commands.py +569 -0
  465. synapse/replication/tcp/context.py +41 -0
  466. synapse/replication/tcp/external_cache.py +156 -0
  467. synapse/replication/tcp/handler.py +922 -0
  468. synapse/replication/tcp/protocol.py +611 -0
  469. synapse/replication/tcp/redis.py +510 -0
  470. synapse/replication/tcp/resource.py +349 -0
  471. synapse/replication/tcp/streams/__init__.py +96 -0
  472. synapse/replication/tcp/streams/_base.py +765 -0
  473. synapse/replication/tcp/streams/events.py +287 -0
  474. synapse/replication/tcp/streams/federation.py +92 -0
  475. synapse/replication/tcp/streams/partial_state.py +80 -0
  476. synapse/res/providers.json +29 -0
  477. synapse/res/templates/_base.html +29 -0
  478. synapse/res/templates/account_previously_renewed.html +6 -0
  479. synapse/res/templates/account_renewed.html +6 -0
  480. synapse/res/templates/add_threepid.html +8 -0
  481. synapse/res/templates/add_threepid.txt +6 -0
  482. synapse/res/templates/add_threepid_failure.html +7 -0
  483. synapse/res/templates/add_threepid_success.html +6 -0
  484. synapse/res/templates/already_in_use.html +12 -0
  485. synapse/res/templates/already_in_use.txt +10 -0
  486. synapse/res/templates/auth_success.html +21 -0
  487. synapse/res/templates/invalid_token.html +6 -0
  488. synapse/res/templates/mail-Element.css +7 -0
  489. synapse/res/templates/mail-Vector.css +7 -0
  490. synapse/res/templates/mail-expiry.css +4 -0
  491. synapse/res/templates/mail.css +156 -0
  492. synapse/res/templates/notice_expiry.html +46 -0
  493. synapse/res/templates/notice_expiry.txt +7 -0
  494. synapse/res/templates/notif.html +51 -0
  495. synapse/res/templates/notif.txt +22 -0
  496. synapse/res/templates/notif_mail.html +59 -0
  497. synapse/res/templates/notif_mail.txt +10 -0
  498. synapse/res/templates/password_reset.html +10 -0
  499. synapse/res/templates/password_reset.txt +7 -0
  500. synapse/res/templates/password_reset_confirmation.html +15 -0
  501. synapse/res/templates/password_reset_failure.html +7 -0
  502. synapse/res/templates/password_reset_success.html +6 -0
  503. synapse/res/templates/recaptcha.html +42 -0
  504. synapse/res/templates/registration.html +12 -0
  505. synapse/res/templates/registration.txt +10 -0
  506. synapse/res/templates/registration_failure.html +6 -0
  507. synapse/res/templates/registration_success.html +6 -0
  508. synapse/res/templates/registration_token.html +18 -0
  509. synapse/res/templates/room.html +33 -0
  510. synapse/res/templates/room.txt +9 -0
  511. synapse/res/templates/sso.css +129 -0
  512. synapse/res/templates/sso_account_deactivated.html +25 -0
  513. synapse/res/templates/sso_auth_account_details.html +186 -0
  514. synapse/res/templates/sso_auth_account_details.js +116 -0
  515. synapse/res/templates/sso_auth_bad_user.html +26 -0
  516. synapse/res/templates/sso_auth_confirm.html +27 -0
  517. synapse/res/templates/sso_auth_success.html +26 -0
  518. synapse/res/templates/sso_error.html +71 -0
  519. synapse/res/templates/sso_footer.html +19 -0
  520. synapse/res/templates/sso_login_idp_picker.html +60 -0
  521. synapse/res/templates/sso_new_user_consent.html +30 -0
  522. synapse/res/templates/sso_partial_profile.html +19 -0
  523. synapse/res/templates/sso_redirect_confirm.html +39 -0
  524. synapse/res/templates/style.css +33 -0
  525. synapse/res/templates/terms.html +27 -0
  526. synapse/rest/__init__.py +197 -0
  527. synapse/rest/admin/__init__.py +395 -0
  528. synapse/rest/admin/_base.py +72 -0
  529. synapse/rest/admin/background_updates.py +171 -0
  530. synapse/rest/admin/devices.py +221 -0
  531. synapse/rest/admin/event_reports.py +173 -0
  532. synapse/rest/admin/events.py +69 -0
  533. synapse/rest/admin/experimental_features.py +137 -0
  534. synapse/rest/admin/federation.py +243 -0
  535. synapse/rest/admin/media.py +540 -0
  536. synapse/rest/admin/registration_tokens.py +358 -0
  537. synapse/rest/admin/rooms.py +1092 -0
  538. synapse/rest/admin/scheduled_tasks.py +70 -0
  539. synapse/rest/admin/server_notice_servlet.py +132 -0
  540. synapse/rest/admin/statistics.py +132 -0
  541. synapse/rest/admin/username_available.py +58 -0
  542. synapse/rest/admin/users.py +1634 -0
  543. synapse/rest/client/__init__.py +20 -0
  544. synapse/rest/client/_base.py +113 -0
  545. synapse/rest/client/account.py +937 -0
  546. synapse/rest/client/account_data.py +319 -0
  547. synapse/rest/client/account_validity.py +103 -0
  548. synapse/rest/client/appservice_ping.py +125 -0
  549. synapse/rest/client/auth.py +218 -0
  550. synapse/rest/client/auth_metadata.py +122 -0
  551. synapse/rest/client/capabilities.py +121 -0
  552. synapse/rest/client/delayed_events.py +165 -0
  553. synapse/rest/client/devices.py +587 -0
  554. synapse/rest/client/directory.py +211 -0
  555. synapse/rest/client/events.py +116 -0
  556. synapse/rest/client/filter.py +112 -0
  557. synapse/rest/client/initial_sync.py +65 -0
  558. synapse/rest/client/keys.py +678 -0
  559. synapse/rest/client/knock.py +104 -0
  560. synapse/rest/client/login.py +750 -0
  561. synapse/rest/client/login_token_request.py +127 -0
  562. synapse/rest/client/logout.py +93 -0
  563. synapse/rest/client/matrixrtc.py +52 -0
  564. synapse/rest/client/media.py +307 -0
  565. synapse/rest/client/mutual_rooms.py +145 -0
  566. synapse/rest/client/notifications.py +137 -0
  567. synapse/rest/client/openid.py +109 -0
  568. synapse/rest/client/password_policy.py +69 -0
  569. synapse/rest/client/presence.py +131 -0
  570. synapse/rest/client/profile.py +291 -0
  571. synapse/rest/client/push_rule.py +331 -0
  572. synapse/rest/client/pusher.py +181 -0
  573. synapse/rest/client/read_marker.py +104 -0
  574. synapse/rest/client/receipts.py +165 -0
  575. synapse/rest/client/register.py +1072 -0
  576. synapse/rest/client/relations.py +138 -0
  577. synapse/rest/client/rendezvous.py +76 -0
  578. synapse/rest/client/reporting.py +207 -0
  579. synapse/rest/client/room.py +1763 -0
  580. synapse/rest/client/room_keys.py +426 -0
  581. synapse/rest/client/room_upgrade_rest_servlet.py +112 -0
  582. synapse/rest/client/sendtodevice.py +85 -0
  583. synapse/rest/client/sync.py +1131 -0
  584. synapse/rest/client/tags.py +129 -0
  585. synapse/rest/client/thirdparty.py +130 -0
  586. synapse/rest/client/thread_subscriptions.py +247 -0
  587. synapse/rest/client/tokenrefresh.py +52 -0
  588. synapse/rest/client/transactions.py +150 -0
  589. synapse/rest/client/user_directory.py +99 -0
  590. synapse/rest/client/versions.py +193 -0
  591. synapse/rest/client/voip.py +88 -0
  592. synapse/rest/consent/__init__.py +0 -0
  593. synapse/rest/consent/consent_resource.py +210 -0
  594. synapse/rest/health.py +38 -0
  595. synapse/rest/key/__init__.py +20 -0
  596. synapse/rest/key/v2/__init__.py +40 -0
  597. synapse/rest/key/v2/local_key_resource.py +125 -0
  598. synapse/rest/key/v2/remote_key_resource.py +302 -0
  599. synapse/rest/media/__init__.py +0 -0
  600. synapse/rest/media/config_resource.py +53 -0
  601. synapse/rest/media/create_resource.py +90 -0
  602. synapse/rest/media/download_resource.py +110 -0
  603. synapse/rest/media/media_repository_resource.py +113 -0
  604. synapse/rest/media/preview_url_resource.py +77 -0
  605. synapse/rest/media/thumbnail_resource.py +142 -0
  606. synapse/rest/media/upload_resource.py +187 -0
  607. synapse/rest/media/v1/__init__.py +39 -0
  608. synapse/rest/media/v1/_base.py +23 -0
  609. synapse/rest/media/v1/media_storage.py +23 -0
  610. synapse/rest/media/v1/storage_provider.py +23 -0
  611. synapse/rest/synapse/__init__.py +20 -0
  612. synapse/rest/synapse/client/__init__.py +93 -0
  613. synapse/rest/synapse/client/federation_whitelist.py +66 -0
  614. synapse/rest/synapse/client/jwks.py +77 -0
  615. synapse/rest/synapse/client/new_user_consent.py +115 -0
  616. synapse/rest/synapse/client/oidc/__init__.py +45 -0
  617. synapse/rest/synapse/client/oidc/backchannel_logout_resource.py +42 -0
  618. synapse/rest/synapse/client/oidc/callback_resource.py +48 -0
  619. synapse/rest/synapse/client/password_reset.py +129 -0
  620. synapse/rest/synapse/client/pick_idp.py +107 -0
  621. synapse/rest/synapse/client/pick_username.py +153 -0
  622. synapse/rest/synapse/client/rendezvous.py +58 -0
  623. synapse/rest/synapse/client/saml2/__init__.py +42 -0
  624. synapse/rest/synapse/client/saml2/metadata_resource.py +46 -0
  625. synapse/rest/synapse/client/saml2/response_resource.py +52 -0
  626. synapse/rest/synapse/client/sso_register.py +56 -0
  627. synapse/rest/synapse/client/unsubscribe.py +88 -0
  628. synapse/rest/synapse/mas/__init__.py +71 -0
  629. synapse/rest/synapse/mas/_base.py +55 -0
  630. synapse/rest/synapse/mas/devices.py +239 -0
  631. synapse/rest/synapse/mas/users.py +469 -0
  632. synapse/rest/well_known.py +148 -0
  633. synapse/server.py +1279 -0
  634. synapse/server_notices/__init__.py +0 -0
  635. synapse/server_notices/consent_server_notices.py +136 -0
  636. synapse/server_notices/resource_limits_server_notices.py +215 -0
  637. synapse/server_notices/server_notices_manager.py +388 -0
  638. synapse/server_notices/server_notices_sender.py +67 -0
  639. synapse/server_notices/worker_server_notices_sender.py +46 -0
  640. synapse/spam_checker_api/__init__.py +31 -0
  641. synapse/state/__init__.py +1023 -0
  642. synapse/state/v1.py +369 -0
  643. synapse/state/v2.py +985 -0
  644. synapse/static/client/login/index.html +47 -0
  645. synapse/static/client/login/js/jquery-3.4.1.min.js +2 -0
  646. synapse/static/client/login/js/login.js +291 -0
  647. synapse/static/client/login/spinner.gif +0 -0
  648. synapse/static/client/login/style.css +79 -0
  649. synapse/static/index.html +63 -0
  650. synapse/storage/__init__.py +43 -0
  651. synapse/storage/_base.py +245 -0
  652. synapse/storage/admin_client_config.py +25 -0
  653. synapse/storage/background_updates.py +1189 -0
  654. synapse/storage/controllers/__init__.py +57 -0
  655. synapse/storage/controllers/persist_events.py +1237 -0
  656. synapse/storage/controllers/purge_events.py +456 -0
  657. synapse/storage/controllers/state.py +950 -0
  658. synapse/storage/controllers/stats.py +119 -0
  659. synapse/storage/database.py +2720 -0
  660. synapse/storage/databases/__init__.py +175 -0
  661. synapse/storage/databases/main/__init__.py +420 -0
  662. synapse/storage/databases/main/account_data.py +1073 -0
  663. synapse/storage/databases/main/appservice.py +473 -0
  664. synapse/storage/databases/main/cache.py +912 -0
  665. synapse/storage/databases/main/censor_events.py +226 -0
  666. synapse/storage/databases/main/client_ips.py +816 -0
  667. synapse/storage/databases/main/delayed_events.py +577 -0
  668. synapse/storage/databases/main/deviceinbox.py +1272 -0
  669. synapse/storage/databases/main/devices.py +2579 -0
  670. synapse/storage/databases/main/directory.py +212 -0
  671. synapse/storage/databases/main/e2e_room_keys.py +689 -0
  672. synapse/storage/databases/main/end_to_end_keys.py +1894 -0
  673. synapse/storage/databases/main/event_federation.py +2511 -0
  674. synapse/storage/databases/main/event_push_actions.py +1936 -0
  675. synapse/storage/databases/main/events.py +3765 -0
  676. synapse/storage/databases/main/events_bg_updates.py +2910 -0
  677. synapse/storage/databases/main/events_forward_extremities.py +126 -0
  678. synapse/storage/databases/main/events_worker.py +2787 -0
  679. synapse/storage/databases/main/experimental_features.py +130 -0
  680. synapse/storage/databases/main/filtering.py +231 -0
  681. synapse/storage/databases/main/keys.py +291 -0
  682. synapse/storage/databases/main/lock.py +554 -0
  683. synapse/storage/databases/main/media_repository.py +1068 -0
  684. synapse/storage/databases/main/metrics.py +461 -0
  685. synapse/storage/databases/main/monthly_active_users.py +443 -0
  686. synapse/storage/databases/main/openid.py +60 -0
  687. synapse/storage/databases/main/presence.py +509 -0
  688. synapse/storage/databases/main/profile.py +539 -0
  689. synapse/storage/databases/main/purge_events.py +521 -0
  690. synapse/storage/databases/main/push_rule.py +970 -0
  691. synapse/storage/databases/main/pusher.py +793 -0
  692. synapse/storage/databases/main/receipts.py +1341 -0
  693. synapse/storage/databases/main/registration.py +3071 -0
  694. synapse/storage/databases/main/rejections.py +37 -0
  695. synapse/storage/databases/main/relations.py +1116 -0
  696. synapse/storage/databases/main/room.py +2779 -0
  697. synapse/storage/databases/main/roommember.py +2132 -0
  698. synapse/storage/databases/main/search.py +939 -0
  699. synapse/storage/databases/main/session.py +152 -0
  700. synapse/storage/databases/main/signatures.py +94 -0
  701. synapse/storage/databases/main/sliding_sync.py +842 -0
  702. synapse/storage/databases/main/state.py +1002 -0
  703. synapse/storage/databases/main/state_deltas.py +360 -0
  704. synapse/storage/databases/main/stats.py +789 -0
  705. synapse/storage/databases/main/stream.py +2589 -0
  706. synapse/storage/databases/main/tags.py +360 -0
  707. synapse/storage/databases/main/task_scheduler.py +225 -0
  708. synapse/storage/databases/main/thread_subscriptions.py +589 -0
  709. synapse/storage/databases/main/transactions.py +676 -0
  710. synapse/storage/databases/main/ui_auth.py +420 -0
  711. synapse/storage/databases/main/user_directory.py +1330 -0
  712. synapse/storage/databases/main/user_erasure_store.py +117 -0
  713. synapse/storage/databases/state/__init__.py +22 -0
  714. synapse/storage/databases/state/bg_updates.py +497 -0
  715. synapse/storage/databases/state/deletion.py +557 -0
  716. synapse/storage/databases/state/store.py +948 -0
  717. synapse/storage/engines/__init__.py +70 -0
  718. synapse/storage/engines/_base.py +154 -0
  719. synapse/storage/engines/postgres.py +261 -0
  720. synapse/storage/engines/sqlite.py +199 -0
  721. synapse/storage/invite_rule.py +152 -0
  722. synapse/storage/keys.py +40 -0
  723. synapse/storage/prepare_database.py +730 -0
  724. synapse/storage/push_rule.py +28 -0
  725. synapse/storage/roommember.py +88 -0
  726. synapse/storage/schema/README.md +4 -0
  727. synapse/storage/schema/__init__.py +186 -0
  728. synapse/storage/schema/common/delta/25/00background_updates.sql +40 -0
  729. synapse/storage/schema/common/delta/35/00background_updates_add_col.sql +36 -0
  730. synapse/storage/schema/common/delta/58/00background_update_ordering.sql +38 -0
  731. synapse/storage/schema/common/full_schemas/72/full.sql.postgres +8 -0
  732. synapse/storage/schema/common/full_schemas/72/full.sql.sqlite +6 -0
  733. synapse/storage/schema/common/schema_version.sql +60 -0
  734. synapse/storage/schema/main/delta/12/v12.sql +82 -0
  735. synapse/storage/schema/main/delta/13/v13.sql +38 -0
  736. synapse/storage/schema/main/delta/14/v14.sql +42 -0
  737. synapse/storage/schema/main/delta/15/appservice_txns.sql +50 -0
  738. synapse/storage/schema/main/delta/15/presence_indices.sql +2 -0
  739. synapse/storage/schema/main/delta/15/v15.sql +24 -0
  740. synapse/storage/schema/main/delta/16/events_order_index.sql +4 -0
  741. synapse/storage/schema/main/delta/16/remote_media_cache_index.sql +2 -0
  742. synapse/storage/schema/main/delta/16/remove_duplicates.sql +9 -0
  743. synapse/storage/schema/main/delta/16/room_alias_index.sql +3 -0
  744. synapse/storage/schema/main/delta/16/unique_constraints.sql +72 -0
  745. synapse/storage/schema/main/delta/16/users.sql +56 -0
  746. synapse/storage/schema/main/delta/17/drop_indexes.sql +37 -0
  747. synapse/storage/schema/main/delta/17/server_keys.sql +43 -0
  748. synapse/storage/schema/main/delta/17/user_threepids.sql +9 -0
  749. synapse/storage/schema/main/delta/18/server_keys_bigger_ints.sql +51 -0
  750. synapse/storage/schema/main/delta/19/event_index.sql +38 -0
  751. synapse/storage/schema/main/delta/20/dummy.sql +1 -0
  752. synapse/storage/schema/main/delta/20/pushers.py +93 -0
  753. synapse/storage/schema/main/delta/21/end_to_end_keys.sql +53 -0
  754. synapse/storage/schema/main/delta/21/receipts.sql +57 -0
  755. synapse/storage/schema/main/delta/22/receipts_index.sql +41 -0
  756. synapse/storage/schema/main/delta/22/user_threepids_unique.sql +19 -0
  757. synapse/storage/schema/main/delta/24/stats_reporting.sql +37 -0
  758. synapse/storage/schema/main/delta/25/fts.py +81 -0
  759. synapse/storage/schema/main/delta/25/guest_access.sql +44 -0
  760. synapse/storage/schema/main/delta/25/history_visibility.sql +44 -0
  761. synapse/storage/schema/main/delta/25/tags.sql +57 -0
  762. synapse/storage/schema/main/delta/26/account_data.sql +36 -0
  763. synapse/storage/schema/main/delta/27/account_data.sql +55 -0
  764. synapse/storage/schema/main/delta/27/forgotten_memberships.sql +45 -0
  765. synapse/storage/schema/main/delta/27/ts.py +61 -0
  766. synapse/storage/schema/main/delta/28/event_push_actions.sql +46 -0
  767. synapse/storage/schema/main/delta/28/events_room_stream.sql +39 -0
  768. synapse/storage/schema/main/delta/28/public_roms_index.sql +39 -0
  769. synapse/storage/schema/main/delta/28/receipts_user_id_index.sql +41 -0
  770. synapse/storage/schema/main/delta/28/upgrade_times.sql +40 -0
  771. synapse/storage/schema/main/delta/28/users_is_guest.sql +41 -0
  772. synapse/storage/schema/main/delta/29/push_actions.sql +54 -0
  773. synapse/storage/schema/main/delta/30/alias_creator.sql +35 -0
  774. synapse/storage/schema/main/delta/30/as_users.py +82 -0
  775. synapse/storage/schema/main/delta/30/deleted_pushers.sql +44 -0
  776. synapse/storage/schema/main/delta/30/presence_stream.sql +49 -0
  777. synapse/storage/schema/main/delta/30/public_rooms.sql +42 -0
  778. synapse/storage/schema/main/delta/30/push_rule_stream.sql +57 -0
  779. synapse/storage/schema/main/delta/30/threepid_guest_access_tokens.sql +43 -0
  780. synapse/storage/schema/main/delta/31/invites.sql +61 -0
  781. synapse/storage/schema/main/delta/31/local_media_repository_url_cache.sql +46 -0
  782. synapse/storage/schema/main/delta/31/pushers_0.py +92 -0
  783. synapse/storage/schema/main/delta/31/pushers_index.sql +41 -0
  784. synapse/storage/schema/main/delta/31/search_update.py +65 -0
  785. synapse/storage/schema/main/delta/32/events.sql +35 -0
  786. synapse/storage/schema/main/delta/32/openid.sql +9 -0
  787. synapse/storage/schema/main/delta/32/pusher_throttle.sql +42 -0
  788. synapse/storage/schema/main/delta/32/remove_indices.sql +52 -0
  789. synapse/storage/schema/main/delta/32/reports.sql +44 -0
  790. synapse/storage/schema/main/delta/33/access_tokens_device_index.sql +36 -0
  791. synapse/storage/schema/main/delta/33/devices.sql +40 -0
  792. synapse/storage/schema/main/delta/33/devices_for_e2e_keys.sql +38 -0
  793. synapse/storage/schema/main/delta/33/devices_for_e2e_keys_clear_unknown_device.sql +39 -0
  794. synapse/storage/schema/main/delta/33/event_fields.py +61 -0
  795. synapse/storage/schema/main/delta/33/remote_media_ts.py +43 -0
  796. synapse/storage/schema/main/delta/33/user_ips_index.sql +36 -0
  797. synapse/storage/schema/main/delta/34/appservice_stream.sql +42 -0
  798. synapse/storage/schema/main/delta/34/cache_stream.py +50 -0
  799. synapse/storage/schema/main/delta/34/device_inbox.sql +43 -0
  800. synapse/storage/schema/main/delta/34/push_display_name_rename.sql +39 -0
  801. synapse/storage/schema/main/delta/34/received_txn_purge.py +36 -0
  802. synapse/storage/schema/main/delta/35/contains_url.sql +36 -0
  803. synapse/storage/schema/main/delta/35/device_outbox.sql +58 -0
  804. synapse/storage/schema/main/delta/35/device_stream_id.sql +40 -0
  805. synapse/storage/schema/main/delta/35/event_push_actions_index.sql +36 -0
  806. synapse/storage/schema/main/delta/35/public_room_list_change_stream.sql +52 -0
  807. synapse/storage/schema/main/delta/35/stream_order_to_extrem.sql +56 -0
  808. synapse/storage/schema/main/delta/36/readd_public_rooms.sql +45 -0
  809. synapse/storage/schema/main/delta/37/remove_auth_idx.py +89 -0
  810. synapse/storage/schema/main/delta/37/user_threepids.sql +71 -0
  811. synapse/storage/schema/main/delta/38/postgres_fts_gist.sql +38 -0
  812. synapse/storage/schema/main/delta/39/appservice_room_list.sql +48 -0
  813. synapse/storage/schema/main/delta/39/device_federation_stream_idx.sql +35 -0
  814. synapse/storage/schema/main/delta/39/event_push_index.sql +36 -0
  815. synapse/storage/schema/main/delta/39/federation_out_position.sql +41 -0
  816. synapse/storage/schema/main/delta/39/membership_profile.sql +39 -0
  817. synapse/storage/schema/main/delta/40/current_state_idx.sql +36 -0
  818. synapse/storage/schema/main/delta/40/device_inbox.sql +40 -0
  819. synapse/storage/schema/main/delta/40/device_list_streams.sql +79 -0
  820. synapse/storage/schema/main/delta/40/event_push_summary.sql +57 -0
  821. synapse/storage/schema/main/delta/40/pushers.sql +58 -0
  822. synapse/storage/schema/main/delta/41/device_list_stream_idx.sql +36 -0
  823. synapse/storage/schema/main/delta/41/device_outbound_index.sql +35 -0
  824. synapse/storage/schema/main/delta/41/event_search_event_id_idx.sql +36 -0
  825. synapse/storage/schema/main/delta/41/ratelimit.sql +41 -0
  826. synapse/storage/schema/main/delta/42/current_state_delta.sql +48 -0
  827. synapse/storage/schema/main/delta/42/device_list_last_id.sql +52 -0
  828. synapse/storage/schema/main/delta/42/event_auth_state_only.sql +36 -0
  829. synapse/storage/schema/main/delta/42/user_dir.py +88 -0
  830. synapse/storage/schema/main/delta/43/blocked_rooms.sql +40 -0
  831. synapse/storage/schema/main/delta/43/quarantine_media.sql +36 -0
  832. synapse/storage/schema/main/delta/43/url_cache.sql +35 -0
  833. synapse/storage/schema/main/delta/43/user_share.sql +52 -0
  834. synapse/storage/schema/main/delta/44/expire_url_cache.sql +60 -0
  835. synapse/storage/schema/main/delta/45/group_server.sql +186 -0
  836. synapse/storage/schema/main/delta/45/profile_cache.sql +47 -0
  837. synapse/storage/schema/main/delta/46/drop_refresh_tokens.sql +36 -0
  838. synapse/storage/schema/main/delta/46/drop_unique_deleted_pushers.sql +54 -0
  839. synapse/storage/schema/main/delta/46/group_server.sql +51 -0
  840. synapse/storage/schema/main/delta/46/local_media_repository_url_idx.sql +43 -0
  841. synapse/storage/schema/main/delta/46/user_dir_null_room_ids.sql +54 -0
  842. synapse/storage/schema/main/delta/46/user_dir_typos.sql +43 -0
  843. synapse/storage/schema/main/delta/47/last_access_media.sql +35 -0
  844. synapse/storage/schema/main/delta/47/postgres_fts_gin.sql +36 -0
  845. synapse/storage/schema/main/delta/47/push_actions_staging.sql +47 -0
  846. synapse/storage/schema/main/delta/48/add_user_consent.sql +37 -0
  847. synapse/storage/schema/main/delta/48/add_user_ips_last_seen_index.sql +36 -0
  848. synapse/storage/schema/main/delta/48/deactivated_users.sql +44 -0
  849. synapse/storage/schema/main/delta/48/group_unique_indexes.py +67 -0
  850. synapse/storage/schema/main/delta/48/groups_joinable.sql +41 -0
  851. synapse/storage/schema/main/delta/49/add_user_consent_server_notice_sent.sql +39 -0
  852. synapse/storage/schema/main/delta/49/add_user_daily_visits.sql +40 -0
  853. synapse/storage/schema/main/delta/49/add_user_ips_last_seen_only_index.sql +36 -0
  854. synapse/storage/schema/main/delta/50/add_creation_ts_users_index.sql +38 -0
  855. synapse/storage/schema/main/delta/50/erasure_store.sql +40 -0
  856. synapse/storage/schema/main/delta/50/make_event_content_nullable.py +102 -0
  857. synapse/storage/schema/main/delta/51/e2e_room_keys.sql +58 -0
  858. synapse/storage/schema/main/delta/51/monthly_active_users.sql +46 -0
  859. synapse/storage/schema/main/delta/52/add_event_to_state_group_index.sql +38 -0
  860. synapse/storage/schema/main/delta/52/device_list_streams_unique_idx.sql +55 -0
  861. synapse/storage/schema/main/delta/52/e2e_room_keys.sql +72 -0
  862. synapse/storage/schema/main/delta/53/add_user_type_to_users.sql +38 -0
  863. synapse/storage/schema/main/delta/53/drop_sent_transactions.sql +35 -0
  864. synapse/storage/schema/main/delta/53/event_format_version.sql +35 -0
  865. synapse/storage/schema/main/delta/53/user_dir_populate.sql +49 -0
  866. synapse/storage/schema/main/delta/53/user_ips_index.sql +49 -0
  867. synapse/storage/schema/main/delta/53/user_share.sql +63 -0
  868. synapse/storage/schema/main/delta/53/user_threepid_id.sql +48 -0
  869. synapse/storage/schema/main/delta/53/users_in_public_rooms.sql +47 -0
  870. synapse/storage/schema/main/delta/54/account_validity_with_renewal.sql +49 -0
  871. synapse/storage/schema/main/delta/54/add_validity_to_server_keys.sql +42 -0
  872. synapse/storage/schema/main/delta/54/delete_forward_extremities.sql +42 -0
  873. synapse/storage/schema/main/delta/54/drop_legacy_tables.sql +49 -0
  874. synapse/storage/schema/main/delta/54/drop_presence_list.sql +35 -0
  875. synapse/storage/schema/main/delta/54/relations.sql +46 -0
  876. synapse/storage/schema/main/delta/54/stats.sql +99 -0
  877. synapse/storage/schema/main/delta/54/stats2.sql +47 -0
  878. synapse/storage/schema/main/delta/55/access_token_expiry.sql +37 -0
  879. synapse/storage/schema/main/delta/55/track_threepid_validations.sql +50 -0
  880. synapse/storage/schema/main/delta/55/users_alter_deactivated.sql +38 -0
  881. synapse/storage/schema/main/delta/56/add_spans_to_device_lists.sql +39 -0
  882. synapse/storage/schema/main/delta/56/current_state_events_membership.sql +41 -0
  883. synapse/storage/schema/main/delta/56/current_state_events_membership_mk2.sql +43 -0
  884. synapse/storage/schema/main/delta/56/delete_keys_from_deleted_backups.sql +44 -0
  885. synapse/storage/schema/main/delta/56/destinations_failure_ts.sql +44 -0
  886. synapse/storage/schema/main/delta/56/destinations_retry_interval_type.sql.postgres +18 -0
  887. synapse/storage/schema/main/delta/56/device_stream_id_insert.sql +39 -0
  888. synapse/storage/schema/main/delta/56/devices_last_seen.sql +43 -0
  889. synapse/storage/schema/main/delta/56/drop_unused_event_tables.sql +39 -0
  890. synapse/storage/schema/main/delta/56/event_expiry.sql +40 -0
  891. synapse/storage/schema/main/delta/56/event_labels.sql +49 -0
  892. synapse/storage/schema/main/delta/56/event_labels_background_update.sql +36 -0
  893. synapse/storage/schema/main/delta/56/fix_room_keys_index.sql +37 -0
  894. synapse/storage/schema/main/delta/56/hidden_devices.sql +37 -0
  895. synapse/storage/schema/main/delta/56/hidden_devices_fix.sql.sqlite +42 -0
  896. synapse/storage/schema/main/delta/56/nuke_empty_communities_from_db.sql +48 -0
  897. synapse/storage/schema/main/delta/56/public_room_list_idx.sql +35 -0
  898. synapse/storage/schema/main/delta/56/redaction_censor.sql +35 -0
  899. synapse/storage/schema/main/delta/56/redaction_censor2.sql +41 -0
  900. synapse/storage/schema/main/delta/56/redaction_censor3_fix_update.sql.postgres +25 -0
  901. synapse/storage/schema/main/delta/56/redaction_censor4.sql +35 -0
  902. synapse/storage/schema/main/delta/56/remove_tombstoned_rooms_from_directory.sql +38 -0
  903. synapse/storage/schema/main/delta/56/room_key_etag.sql +36 -0
  904. synapse/storage/schema/main/delta/56/room_membership_idx.sql +37 -0
  905. synapse/storage/schema/main/delta/56/room_retention.sql +52 -0
  906. synapse/storage/schema/main/delta/56/signing_keys.sql +75 -0
  907. synapse/storage/schema/main/delta/56/signing_keys_nonunique_signatures.sql +41 -0
  908. synapse/storage/schema/main/delta/56/stats_separated.sql +175 -0
  909. synapse/storage/schema/main/delta/56/unique_user_filter_index.py +46 -0
  910. synapse/storage/schema/main/delta/56/user_external_ids.sql +43 -0
  911. synapse/storage/schema/main/delta/56/users_in_public_rooms_idx.sql +36 -0
  912. synapse/storage/schema/main/delta/57/delete_old_current_state_events.sql +41 -0
  913. synapse/storage/schema/main/delta/57/device_list_remote_cache_stale.sql +44 -0
  914. synapse/storage/schema/main/delta/57/local_current_membership.py +111 -0
  915. synapse/storage/schema/main/delta/57/remove_sent_outbound_pokes.sql +40 -0
  916. synapse/storage/schema/main/delta/57/rooms_version_column.sql +43 -0
  917. synapse/storage/schema/main/delta/57/rooms_version_column_2.sql.postgres +35 -0
  918. synapse/storage/schema/main/delta/57/rooms_version_column_2.sql.sqlite +22 -0
  919. synapse/storage/schema/main/delta/57/rooms_version_column_3.sql.postgres +39 -0
  920. synapse/storage/schema/main/delta/57/rooms_version_column_3.sql.sqlite +23 -0
  921. synapse/storage/schema/main/delta/58/02remove_dup_outbound_pokes.sql +41 -0
  922. synapse/storage/schema/main/delta/58/03persist_ui_auth.sql +55 -0
  923. synapse/storage/schema/main/delta/58/05cache_instance.sql.postgres +30 -0
  924. synapse/storage/schema/main/delta/58/06dlols_unique_idx.py +83 -0
  925. synapse/storage/schema/main/delta/58/07add_method_to_thumbnail_constraint.sql.postgres +33 -0
  926. synapse/storage/schema/main/delta/58/07add_method_to_thumbnail_constraint.sql.sqlite +44 -0
  927. synapse/storage/schema/main/delta/58/07persist_ui_auth_ips.sql +44 -0
  928. synapse/storage/schema/main/delta/58/08_media_safe_from_quarantine.sql.postgres +18 -0
  929. synapse/storage/schema/main/delta/58/08_media_safe_from_quarantine.sql.sqlite +18 -0
  930. synapse/storage/schema/main/delta/58/09shadow_ban.sql +37 -0
  931. synapse/storage/schema/main/delta/58/10_pushrules_enabled_delete_obsolete.sql +47 -0
  932. synapse/storage/schema/main/delta/58/10drop_local_rejections_stream.sql +41 -0
  933. synapse/storage/schema/main/delta/58/10federation_pos_instance_name.sql +41 -0
  934. synapse/storage/schema/main/delta/58/11dehydration.sql +39 -0
  935. synapse/storage/schema/main/delta/58/11fallback.sql +43 -0
  936. synapse/storage/schema/main/delta/58/11user_id_seq.py +38 -0
  937. synapse/storage/schema/main/delta/58/12room_stats.sql +51 -0
  938. synapse/storage/schema/main/delta/58/13remove_presence_allow_inbound.sql +36 -0
  939. synapse/storage/schema/main/delta/58/14events_instance_name.sql +35 -0
  940. synapse/storage/schema/main/delta/58/14events_instance_name.sql.postgres +28 -0
  941. synapse/storage/schema/main/delta/58/15_catchup_destination_rooms.sql +61 -0
  942. synapse/storage/schema/main/delta/58/15unread_count.sql +45 -0
  943. synapse/storage/schema/main/delta/58/16populate_stats_process_rooms_fix.sql +41 -0
  944. synapse/storage/schema/main/delta/58/17_catchup_last_successful.sql +40 -0
  945. synapse/storage/schema/main/delta/58/18stream_positions.sql +41 -0
  946. synapse/storage/schema/main/delta/58/19instance_map.sql.postgres +25 -0
  947. synapse/storage/schema/main/delta/58/19txn_id.sql +59 -0
  948. synapse/storage/schema/main/delta/58/20instance_name_event_tables.sql +36 -0
  949. synapse/storage/schema/main/delta/58/20user_daily_visits.sql +37 -0
  950. synapse/storage/schema/main/delta/58/21as_device_stream.sql +36 -0
  951. synapse/storage/schema/main/delta/58/21drop_device_max_stream_id.sql +1 -0
  952. synapse/storage/schema/main/delta/58/22puppet_token.sql +36 -0
  953. synapse/storage/schema/main/delta/58/22users_have_local_media.sql +2 -0
  954. synapse/storage/schema/main/delta/58/23e2e_cross_signing_keys_idx.sql +36 -0
  955. synapse/storage/schema/main/delta/58/24drop_event_json_index.sql +38 -0
  956. synapse/storage/schema/main/delta/58/25user_external_ids_user_id_idx.sql +36 -0
  957. synapse/storage/schema/main/delta/58/26access_token_last_validated.sql +37 -0
  958. synapse/storage/schema/main/delta/58/27local_invites.sql +37 -0
  959. synapse/storage/schema/main/delta/58/28drop_last_used_column.sql.postgres +16 -0
  960. synapse/storage/schema/main/delta/58/28drop_last_used_column.sql.sqlite +62 -0
  961. synapse/storage/schema/main/delta/59/01ignored_user.py +85 -0
  962. synapse/storage/schema/main/delta/59/02shard_send_to_device.sql +37 -0
  963. synapse/storage/schema/main/delta/59/03shard_send_to_device_sequence.sql.postgres +25 -0
  964. synapse/storage/schema/main/delta/59/04_event_auth_chains.sql +71 -0
  965. synapse/storage/schema/main/delta/59/04_event_auth_chains.sql.postgres +16 -0
  966. synapse/storage/schema/main/delta/59/04drop_account_data.sql +36 -0
  967. synapse/storage/schema/main/delta/59/05cache_invalidation.sql +36 -0
  968. synapse/storage/schema/main/delta/59/06chain_cover_index.sql +36 -0
  969. synapse/storage/schema/main/delta/59/06shard_account_data.sql +39 -0
  970. synapse/storage/schema/main/delta/59/06shard_account_data.sql.postgres +32 -0
  971. synapse/storage/schema/main/delta/59/07shard_account_data_fix.sql +37 -0
  972. synapse/storage/schema/main/delta/59/08delete_pushers_for_deactivated_accounts.sql +39 -0
  973. synapse/storage/schema/main/delta/59/08delete_stale_pushers.sql +39 -0
  974. synapse/storage/schema/main/delta/59/09rejected_events_metadata.sql +45 -0
  975. synapse/storage/schema/main/delta/59/10delete_purged_chain_cover.sql +36 -0
  976. synapse/storage/schema/main/delta/59/11add_knock_members_to_stats.sql +39 -0
  977. synapse/storage/schema/main/delta/59/11drop_thumbnail_constraint.sql.postgres +22 -0
  978. synapse/storage/schema/main/delta/59/12account_validity_token_used_ts_ms.sql +37 -0
  979. synapse/storage/schema/main/delta/59/12presence_stream_instance.sql +37 -0
  980. synapse/storage/schema/main/delta/59/12presence_stream_instance_seq.sql.postgres +20 -0
  981. synapse/storage/schema/main/delta/59/13users_to_send_full_presence_to.sql +53 -0
  982. synapse/storage/schema/main/delta/59/14refresh_tokens.sql +53 -0
  983. synapse/storage/schema/main/delta/59/15locks.sql +56 -0
  984. synapse/storage/schema/main/delta/59/16federation_inbound_staging.sql +51 -0
  985. synapse/storage/schema/main/delta/60/01recreate_stream_ordering.sql.postgres +45 -0
  986. synapse/storage/schema/main/delta/60/02change_stream_ordering_columns.sql.postgres +30 -0
  987. synapse/storage/schema/main/delta/61/01change_appservices_txns.sql.postgres +23 -0
  988. synapse/storage/schema/main/delta/61/01insertion_event_lookups.sql +68 -0
  989. synapse/storage/schema/main/delta/61/02drop_redundant_room_depth_index.sql +37 -0
  990. synapse/storage/schema/main/delta/61/03recreate_min_depth.py +74 -0
  991. synapse/storage/schema/main/delta/62/01insertion_event_extremities.sql +43 -0
  992. synapse/storage/schema/main/delta/63/01create_registration_tokens.sql +42 -0
  993. synapse/storage/schema/main/delta/63/02delete_unlinked_email_pushers.sql +39 -0
  994. synapse/storage/schema/main/delta/63/02populate-rooms-creator.sql +36 -0
  995. synapse/storage/schema/main/delta/63/03session_store.sql +42 -0
  996. synapse/storage/schema/main/delta/63/04add_presence_stream_not_offline_index.sql +37 -0
  997. synapse/storage/schema/main/delta/64/01msc2716_chunk_to_batch_rename.sql.postgres +23 -0
  998. synapse/storage/schema/main/delta/64/01msc2716_chunk_to_batch_rename.sql.sqlite +37 -0
  999. synapse/storage/schema/main/delta/65/01msc2716_insertion_event_edges.sql +38 -0
  1000. synapse/storage/schema/main/delta/65/03remove_hidden_devices_from_device_inbox.sql +41 -0
  1001. synapse/storage/schema/main/delta/65/04_local_group_updates.sql +37 -0
  1002. synapse/storage/schema/main/delta/65/05_remove_room_stats_historical_and_user_stats_historical.sql +38 -0
  1003. synapse/storage/schema/main/delta/65/06remove_deleted_devices_from_device_inbox.sql +53 -0
  1004. synapse/storage/schema/main/delta/65/07_arbitrary_relations.sql +37 -0
  1005. synapse/storage/schema/main/delta/65/08_device_inbox_background_updates.sql +37 -0
  1006. synapse/storage/schema/main/delta/65/10_expirable_refresh_tokens.sql +47 -0
  1007. synapse/storage/schema/main/delta/65/11_devices_auth_provider_session.sql +46 -0
  1008. synapse/storage/schema/main/delta/67/01drop_public_room_list_stream.sql +37 -0
  1009. synapse/storage/schema/main/delta/68/01event_columns.sql +45 -0
  1010. synapse/storage/schema/main/delta/68/02_msc2409_add_device_id_appservice_stream_type.sql +40 -0
  1011. synapse/storage/schema/main/delta/68/03_delete_account_data_for_deactivated_accounts.sql +39 -0
  1012. synapse/storage/schema/main/delta/68/04_refresh_tokens_index_next_token_id.sql +47 -0
  1013. synapse/storage/schema/main/delta/68/04partial_state_rooms.sql +60 -0
  1014. synapse/storage/schema/main/delta/68/05_delete_non_strings_from_event_search.sql.sqlite +22 -0
  1015. synapse/storage/schema/main/delta/68/05partial_state_rooms_triggers.py +80 -0
  1016. synapse/storage/schema/main/delta/68/06_msc3202_add_device_list_appservice_stream_type.sql +42 -0
  1017. synapse/storage/schema/main/delta/69/01as_txn_seq.py +54 -0
  1018. synapse/storage/schema/main/delta/69/01device_list_oubound_by_room.sql +57 -0
  1019. synapse/storage/schema/main/delta/69/02cache_invalidation_index.sql +37 -0
  1020. synapse/storage/schema/main/delta/70/01clean_table_purged_rooms.sql +39 -0
  1021. synapse/storage/schema/main/delta/71/01rebuild_event_edges.sql.postgres +43 -0
  1022. synapse/storage/schema/main/delta/71/01rebuild_event_edges.sql.sqlite +47 -0
  1023. synapse/storage/schema/main/delta/71/01remove_noop_background_updates.sql +80 -0
  1024. synapse/storage/schema/main/delta/71/02event_push_summary_unique.sql +37 -0
  1025. synapse/storage/schema/main/delta/72/01add_room_type_to_state_stats.sql +38 -0
  1026. synapse/storage/schema/main/delta/72/01event_push_summary_receipt.sql +54 -0
  1027. synapse/storage/schema/main/delta/72/02event_push_actions_index.sql +38 -0
  1028. synapse/storage/schema/main/delta/72/03bg_populate_events_columns.py +57 -0
  1029. synapse/storage/schema/main/delta/72/03drop_event_reference_hashes.sql +36 -0
  1030. synapse/storage/schema/main/delta/72/03remove_groups.sql +50 -0
  1031. synapse/storage/schema/main/delta/72/04drop_column_application_services_state_last_txn.sql.postgres +17 -0
  1032. synapse/storage/schema/main/delta/72/04drop_column_application_services_state_last_txn.sql.sqlite +40 -0
  1033. synapse/storage/schema/main/delta/72/05receipts_event_stream_ordering.sql +38 -0
  1034. synapse/storage/schema/main/delta/72/05remove_unstable_private_read_receipts.sql +38 -0
  1035. synapse/storage/schema/main/delta/72/06add_consent_ts_to_users.sql +35 -0
  1036. synapse/storage/schema/main/delta/72/06thread_notifications.sql +49 -0
  1037. synapse/storage/schema/main/delta/72/07force_update_current_state_events_membership.py +67 -0
  1038. synapse/storage/schema/main/delta/72/07thread_receipts.sql.postgres +30 -0
  1039. synapse/storage/schema/main/delta/72/07thread_receipts.sql.sqlite +70 -0
  1040. synapse/storage/schema/main/delta/72/08begin_cache_invalidation_seq_at_2.sql.postgres +23 -0
  1041. synapse/storage/schema/main/delta/72/08thread_receipts.sql +39 -0
  1042. synapse/storage/schema/main/delta/72/09partial_indices.sql.sqlite +56 -0
  1043. synapse/storage/schema/main/delta/73/01event_failed_pull_attempts.sql +48 -0
  1044. synapse/storage/schema/main/delta/73/02add_pusher_enabled.sql +35 -0
  1045. synapse/storage/schema/main/delta/73/02room_id_indexes_for_purging.sql +41 -0
  1046. synapse/storage/schema/main/delta/73/03pusher_device_id.sql +39 -0
  1047. synapse/storage/schema/main/delta/73/03users_approved_column.sql +39 -0
  1048. synapse/storage/schema/main/delta/73/04partial_join_details.sql +42 -0
  1049. synapse/storage/schema/main/delta/73/04pending_device_list_updates.sql +47 -0
  1050. synapse/storage/schema/main/delta/73/05old_push_actions.sql.postgres +22 -0
  1051. synapse/storage/schema/main/delta/73/05old_push_actions.sql.sqlite +24 -0
  1052. synapse/storage/schema/main/delta/73/06thread_notifications_thread_id_idx.sql +42 -0
  1053. synapse/storage/schema/main/delta/73/08thread_receipts_non_null.sql.postgres +23 -0
  1054. synapse/storage/schema/main/delta/73/08thread_receipts_non_null.sql.sqlite +76 -0
  1055. synapse/storage/schema/main/delta/73/09partial_joined_via_destination.sql +37 -0
  1056. synapse/storage/schema/main/delta/73/09threads_table.sql +49 -0
  1057. synapse/storage/schema/main/delta/73/10_update_sqlite_fts4_tokenizer.py +71 -0
  1058. synapse/storage/schema/main/delta/73/10login_tokens.sql +54 -0
  1059. synapse/storage/schema/main/delta/73/11event_search_room_id_n_distinct.sql.postgres +33 -0
  1060. synapse/storage/schema/main/delta/73/12refactor_device_list_outbound_pokes.sql +72 -0
  1061. synapse/storage/schema/main/delta/73/13add_device_lists_index.sql +39 -0
  1062. synapse/storage/schema/main/delta/73/20_un_partial_stated_room_stream.sql +51 -0
  1063. synapse/storage/schema/main/delta/73/21_un_partial_stated_room_stream_seq.sql.postgres +20 -0
  1064. synapse/storage/schema/main/delta/73/22_rebuild_user_dir_stats.sql +48 -0
  1065. synapse/storage/schema/main/delta/73/22_un_partial_stated_event_stream.sql +53 -0
  1066. synapse/storage/schema/main/delta/73/23_fix_thread_index.sql +52 -0
  1067. synapse/storage/schema/main/delta/73/23_un_partial_stated_room_stream_seq.sql.postgres +20 -0
  1068. synapse/storage/schema/main/delta/73/24_events_jump_to_date_index.sql +36 -0
  1069. synapse/storage/schema/main/delta/73/25drop_presence.sql +36 -0
  1070. synapse/storage/schema/main/delta/74/01_user_directory_stale_remote_users.sql +58 -0
  1071. synapse/storage/schema/main/delta/74/02_set_device_id_for_pushers_bg_update.sql +38 -0
  1072. synapse/storage/schema/main/delta/74/03_membership_tables_event_stream_ordering.sql.postgres +29 -0
  1073. synapse/storage/schema/main/delta/74/03_membership_tables_event_stream_ordering.sql.sqlite +23 -0
  1074. synapse/storage/schema/main/delta/74/03_room_membership_index.sql +38 -0
  1075. synapse/storage/schema/main/delta/74/04_delete_e2e_backup_keys_for_deactivated_users.sql +36 -0
  1076. synapse/storage/schema/main/delta/74/04_membership_tables_event_stream_ordering_triggers.py +87 -0
  1077. synapse/storage/schema/main/delta/74/05_events_txn_id_device_id.sql +72 -0
  1078. synapse/storage/schema/main/delta/74/90COMMENTS_destinations.sql.postgres +52 -0
  1079. synapse/storage/schema/main/delta/76/01_add_profiles_full_user_id_column.sql +39 -0
  1080. synapse/storage/schema/main/delta/76/02_add_user_filters_full_user_id_column.sql +39 -0
  1081. synapse/storage/schema/main/delta/76/03_per_user_experimental_features.sql +46 -0
  1082. synapse/storage/schema/main/delta/76/04_add_room_forgetter.sql +43 -0
  1083. synapse/storage/schema/main/delta/77/01_add_profiles_not_valid_check.sql.postgres +16 -0
  1084. synapse/storage/schema/main/delta/77/02_add_user_filters_not_valid_check.sql.postgres +16 -0
  1085. synapse/storage/schema/main/delta/77/03bg_populate_full_user_id_profiles.sql +35 -0
  1086. synapse/storage/schema/main/delta/77/04bg_populate_full_user_id_user_filters.sql +35 -0
  1087. synapse/storage/schema/main/delta/77/05thread_notifications_backfill.sql +67 -0
  1088. synapse/storage/schema/main/delta/77/06thread_notifications_not_null.sql.sqlite +102 -0
  1089. synapse/storage/schema/main/delta/77/06thread_notifications_not_null_event_push_actions.sql.postgres +27 -0
  1090. synapse/storage/schema/main/delta/77/06thread_notifications_not_null_event_push_actions_staging.sql.postgres +27 -0
  1091. synapse/storage/schema/main/delta/77/06thread_notifications_not_null_event_push_summary.sql.postgres +29 -0
  1092. synapse/storage/schema/main/delta/77/14bg_indices_event_stream_ordering.sql +39 -0
  1093. synapse/storage/schema/main/delta/78/01_validate_and_update_profiles.py +99 -0
  1094. synapse/storage/schema/main/delta/78/02_validate_and_update_user_filters.py +100 -0
  1095. synapse/storage/schema/main/delta/78/03_remove_unused_indexes_user_filters.py +72 -0
  1096. synapse/storage/schema/main/delta/78/03event_extremities_constraints.py +65 -0
  1097. synapse/storage/schema/main/delta/78/04_add_full_user_id_index_user_filters.py +32 -0
  1098. synapse/storage/schema/main/delta/79/03_read_write_locks_triggers.sql.postgres +102 -0
  1099. synapse/storage/schema/main/delta/79/03_read_write_locks_triggers.sql.sqlite +72 -0
  1100. synapse/storage/schema/main/delta/79/04_mitigate_stream_ordering_update_race.py +70 -0
  1101. synapse/storage/schema/main/delta/79/05_read_write_locks_triggers.sql.postgres +69 -0
  1102. synapse/storage/schema/main/delta/79/05_read_write_locks_triggers.sql.sqlite +65 -0
  1103. synapse/storage/schema/main/delta/80/01_users_alter_locked.sql +35 -0
  1104. synapse/storage/schema/main/delta/80/02_read_write_locks_unlogged.sql.postgres +30 -0
  1105. synapse/storage/schema/main/delta/80/02_scheduled_tasks.sql +47 -0
  1106. synapse/storage/schema/main/delta/80/03_read_write_locks_triggers.sql.postgres +37 -0
  1107. synapse/storage/schema/main/delta/80/04_read_write_locks_deadlock.sql.postgres +71 -0
  1108. synapse/storage/schema/main/delta/82/02_scheduled_tasks_index.sql +35 -0
  1109. synapse/storage/schema/main/delta/82/04_add_indices_for_purging_rooms.sql +39 -0
  1110. synapse/storage/schema/main/delta/82/05gaps.sql +44 -0
  1111. synapse/storage/schema/main/delta/83/01_drop_old_tables.sql +43 -0
  1112. synapse/storage/schema/main/delta/83/03_instance_name_receipts.sql.sqlite +17 -0
  1113. synapse/storage/schema/main/delta/83/05_cross_signing_key_update_grant.sql +34 -0
  1114. synapse/storage/schema/main/delta/83/06_event_push_summary_room.sql +36 -0
  1115. synapse/storage/schema/main/delta/84/01_auth_links_stats.sql.postgres +20 -0
  1116. synapse/storage/schema/main/delta/84/02_auth_links_index.sql +16 -0
  1117. synapse/storage/schema/main/delta/84/03_auth_links_analyze.sql.postgres +16 -0
  1118. synapse/storage/schema/main/delta/84/04_access_token_index.sql +15 -0
  1119. synapse/storage/schema/main/delta/85/01_add_suspended.sql +14 -0
  1120. synapse/storage/schema/main/delta/85/02_add_instance_names.sql +27 -0
  1121. synapse/storage/schema/main/delta/85/03_new_sequences.sql.postgres +54 -0
  1122. synapse/storage/schema/main/delta/85/04_cleanup_device_federation_outbox.sql +15 -0
  1123. synapse/storage/schema/main/delta/85/05_add_instance_names_converted_pos.sql +16 -0
  1124. synapse/storage/schema/main/delta/85/06_add_room_reports.sql +20 -0
  1125. synapse/storage/schema/main/delta/86/01_authenticate_media.sql +15 -0
  1126. synapse/storage/schema/main/delta/86/02_receipts_event_id_index.sql +15 -0
  1127. synapse/storage/schema/main/delta/87/01_sliding_sync_memberships.sql +169 -0
  1128. synapse/storage/schema/main/delta/87/02_per_connection_state.sql +81 -0
  1129. synapse/storage/schema/main/delta/87/03_current_state_index.sql +19 -0
  1130. synapse/storage/schema/main/delta/88/01_add_delayed_events.sql +43 -0
  1131. synapse/storage/schema/main/delta/88/01_custom_profile_fields.sql +15 -0
  1132. synapse/storage/schema/main/delta/88/02_fix_sliding_sync_membership_snapshots_forgotten_column.sql +21 -0
  1133. synapse/storage/schema/main/delta/88/03_add_otk_ts_added_index.sql +18 -0
  1134. synapse/storage/schema/main/delta/88/04_current_state_delta_index.sql +18 -0
  1135. synapse/storage/schema/main/delta/88/05_drop_old_otks.sql.postgres +19 -0
  1136. synapse/storage/schema/main/delta/88/05_drop_old_otks.sql.sqlite +19 -0
  1137. synapse/storage/schema/main/delta/88/05_sliding_sync_room_config_index.sql +20 -0
  1138. synapse/storage/schema/main/delta/88/06_events_received_ts_index.sql +17 -0
  1139. synapse/storage/schema/main/delta/89/01_sliding_sync_membership_snapshot_index.sql +15 -0
  1140. synapse/storage/schema/main/delta/90/01_add_column_participant_room_memberships_table.sql +16 -0
  1141. synapse/storage/schema/main/delta/91/01_media_hash.sql +28 -0
  1142. synapse/storage/schema/main/delta/92/01_remove_trigger.sql.postgres +16 -0
  1143. synapse/storage/schema/main/delta/92/01_remove_trigger.sql.sqlite +16 -0
  1144. synapse/storage/schema/main/delta/92/02_remove_populate_participant_bg_update.sql +17 -0
  1145. synapse/storage/schema/main/delta/92/04_ss_membership_snapshot_idx.sql +16 -0
  1146. synapse/storage/schema/main/delta/92/04_thread_subscriptions.sql +59 -0
  1147. synapse/storage/schema/main/delta/92/04_thread_subscriptions_seq.sql.postgres +19 -0
  1148. synapse/storage/schema/main/delta/92/05_fixup_max_depth_cap.sql +17 -0
  1149. synapse/storage/schema/main/delta/92/05_thread_subscriptions_comments.sql.postgres +18 -0
  1150. synapse/storage/schema/main/delta/92/06_device_federation_inbox_index.sql +16 -0
  1151. synapse/storage/schema/main/delta/92/06_threads_last_sent_stream_ordering_comments.sql.postgres +24 -0
  1152. synapse/storage/schema/main/delta/92/07_add_user_reports.sql +22 -0
  1153. synapse/storage/schema/main/delta/92/07_event_txn_id_device_id_txn_id2.sql +15 -0
  1154. synapse/storage/schema/main/delta/92/08_room_ban_redactions.sql +21 -0
  1155. synapse/storage/schema/main/delta/92/08_thread_subscriptions_seq_fixup.sql.postgres +19 -0
  1156. synapse/storage/schema/main/delta/92/09_thread_subscriptions_update.sql +20 -0
  1157. synapse/storage/schema/main/delta/92/09_thread_subscriptions_update.sql.postgres +18 -0
  1158. synapse/storage/schema/main/delta/93/01_add_delayed_events.sql +15 -0
  1159. synapse/storage/schema/main/delta/93/02_sliding_sync_members.sql +60 -0
  1160. synapse/storage/schema/main/delta/93/03_sss_pos_last_used.sql +27 -0
  1161. synapse/storage/schema/main/full_schemas/72/full.sql.postgres +1344 -0
  1162. synapse/storage/schema/main/full_schemas/72/full.sql.sqlite +646 -0
  1163. synapse/storage/schema/state/delta/23/drop_state_index.sql +35 -0
  1164. synapse/storage/schema/state/delta/32/remove_state_indices.sql +38 -0
  1165. synapse/storage/schema/state/delta/35/add_state_index.sql +36 -0
  1166. synapse/storage/schema/state/delta/35/state.sql +41 -0
  1167. synapse/storage/schema/state/delta/35/state_dedupe.sql +36 -0
  1168. synapse/storage/schema/state/delta/47/state_group_seq.py +38 -0
  1169. synapse/storage/schema/state/delta/56/state_group_room_idx.sql +36 -0
  1170. synapse/storage/schema/state/delta/61/02state_groups_state_n_distinct.sql.postgres +34 -0
  1171. synapse/storage/schema/state/delta/70/08_state_group_edges_unique.sql +36 -0
  1172. synapse/storage/schema/state/delta/89/01_state_groups_deletion.sql +39 -0
  1173. synapse/storage/schema/state/delta/90/02_delete_unreferenced_state_groups.sql +16 -0
  1174. synapse/storage/schema/state/delta/90/03_remove_old_deletion_bg_update.sql +15 -0
  1175. synapse/storage/schema/state/full_schemas/72/full.sql.postgres +30 -0
  1176. synapse/storage/schema/state/full_schemas/72/full.sql.sqlite +20 -0
  1177. synapse/storage/types.py +183 -0
  1178. synapse/storage/util/__init__.py +20 -0
  1179. synapse/storage/util/id_generators.py +928 -0
  1180. synapse/storage/util/partial_state_events_tracker.py +194 -0
  1181. synapse/storage/util/sequence.py +315 -0
  1182. synapse/streams/__init__.py +43 -0
  1183. synapse/streams/config.py +91 -0
  1184. synapse/streams/events.py +203 -0
  1185. synapse/synapse_rust/__init__.pyi +3 -0
  1186. synapse/synapse_rust/acl.pyi +20 -0
  1187. synapse/synapse_rust/events.pyi +136 -0
  1188. synapse/synapse_rust/http_client.pyi +32 -0
  1189. synapse/synapse_rust/push.pyi +86 -0
  1190. synapse/synapse_rust/rendezvous.pyi +30 -0
  1191. synapse/synapse_rust/segmenter.pyi +1 -0
  1192. synapse/synapse_rust.abi3.so +0 -0
  1193. synapse/types/__init__.py +1600 -0
  1194. synapse/types/handlers/__init__.py +93 -0
  1195. synapse/types/handlers/policy_server.py +16 -0
  1196. synapse/types/handlers/sliding_sync.py +1004 -0
  1197. synapse/types/rest/__init__.py +25 -0
  1198. synapse/types/rest/client/__init__.py +413 -0
  1199. synapse/types/state.py +634 -0
  1200. synapse/types/storage/__init__.py +66 -0
  1201. synapse/util/__init__.py +160 -0
  1202. synapse/util/async_helpers.py +1048 -0
  1203. synapse/util/background_queue.py +142 -0
  1204. synapse/util/batching_queue.py +203 -0
  1205. synapse/util/caches/__init__.py +300 -0
  1206. synapse/util/caches/cached_call.py +143 -0
  1207. synapse/util/caches/deferred_cache.py +530 -0
  1208. synapse/util/caches/descriptors.py +692 -0
  1209. synapse/util/caches/dictionary_cache.py +346 -0
  1210. synapse/util/caches/expiringcache.py +250 -0
  1211. synapse/util/caches/lrucache.py +976 -0
  1212. synapse/util/caches/response_cache.py +323 -0
  1213. synapse/util/caches/stream_change_cache.py +370 -0
  1214. synapse/util/caches/treecache.py +189 -0
  1215. synapse/util/caches/ttlcache.py +197 -0
  1216. synapse/util/cancellation.py +63 -0
  1217. synapse/util/check_dependencies.py +335 -0
  1218. synapse/util/clock.py +592 -0
  1219. synapse/util/daemonize.py +165 -0
  1220. synapse/util/distributor.py +157 -0
  1221. synapse/util/duration.py +117 -0
  1222. synapse/util/events.py +134 -0
  1223. synapse/util/file_consumer.py +164 -0
  1224. synapse/util/frozenutils.py +57 -0
  1225. synapse/util/gai_resolver.py +178 -0
  1226. synapse/util/hash.py +38 -0
  1227. synapse/util/httpresourcetree.py +108 -0
  1228. synapse/util/iterutils.py +190 -0
  1229. synapse/util/json.py +56 -0
  1230. synapse/util/linked_list.py +156 -0
  1231. synapse/util/logcontext.py +46 -0
  1232. synapse/util/logformatter.py +28 -0
  1233. synapse/util/macaroons.py +325 -0
  1234. synapse/util/manhole.py +191 -0
  1235. synapse/util/metrics.py +339 -0
  1236. synapse/util/module_loader.py +116 -0
  1237. synapse/util/msisdn.py +51 -0
  1238. synapse/util/patch_inline_callbacks.py +250 -0
  1239. synapse/util/pydantic_models.py +63 -0
  1240. synapse/util/ratelimitutils.py +422 -0
  1241. synapse/util/retryutils.py +339 -0
  1242. synapse/util/rlimit.py +42 -0
  1243. synapse/util/rust.py +164 -0
  1244. synapse/util/sentinel.py +21 -0
  1245. synapse/util/stringutils.py +293 -0
  1246. synapse/util/task_scheduler.py +494 -0
  1247. synapse/util/templates.py +126 -0
  1248. synapse/util/threepids.py +123 -0
  1249. synapse/util/wheel_timer.py +112 -0
  1250. synapse/visibility.py +869 -0
  1251. synmark/__init__.py +47 -0
  1252. synmark/__main__.py +128 -0
  1253. synmark/suites/__init__.py +9 -0
  1254. synmark/suites/logging.py +154 -0
  1255. synmark/suites/lrucache.py +48 -0
  1256. synmark/suites/lrucache_evict.py +49 -0
  1257. sytest-blacklist +33 -0
  1258. tests/__init__.py +29 -0
  1259. tests/api/__init__.py +0 -0
  1260. tests/api/test_auth.py +548 -0
  1261. tests/api/test_errors.py +46 -0
  1262. tests/api/test_filtering.py +622 -0
  1263. tests/api/test_ratelimiting.py +505 -0
  1264. tests/api/test_urls.py +81 -0
  1265. tests/app/__init__.py +0 -0
  1266. tests/app/test_homeserver_shutdown.py +271 -0
  1267. tests/app/test_homeserver_start.py +49 -0
  1268. tests/app/test_openid_listener.py +139 -0
  1269. tests/app/test_phone_stats_home.py +248 -0
  1270. tests/appservice/__init__.py +20 -0
  1271. tests/appservice/test_api.py +253 -0
  1272. tests/appservice/test_appservice.py +259 -0
  1273. tests/appservice/test_scheduler.py +477 -0
  1274. tests/config/__init__.py +20 -0
  1275. tests/config/test___main__.py +38 -0
  1276. tests/config/test_api.py +146 -0
  1277. tests/config/test_appservice.py +48 -0
  1278. tests/config/test_background_update.py +65 -0
  1279. tests/config/test_base.py +151 -0
  1280. tests/config/test_cache.py +193 -0
  1281. tests/config/test_database.py +42 -0
  1282. tests/config/test_generate.py +72 -0
  1283. tests/config/test_load.py +322 -0
  1284. tests/config/test_oauth_delegation.py +454 -0
  1285. tests/config/test_ratelimiting.py +77 -0
  1286. tests/config/test_registration_config.py +203 -0
  1287. tests/config/test_room_directory.py +203 -0
  1288. tests/config/test_server.py +248 -0
  1289. tests/config/test_tls.py +209 -0
  1290. tests/config/test_util.py +59 -0
  1291. tests/config/test_workers.py +332 -0
  1292. tests/config/utils.py +66 -0
  1293. tests/crypto/__init__.py +20 -0
  1294. tests/crypto/test_event_signing.py +109 -0
  1295. tests/crypto/test_keyring.py +737 -0
  1296. tests/events/__init__.py +0 -0
  1297. tests/events/test_auto_accept_invites.py +827 -0
  1298. tests/events/test_presence_router.py +540 -0
  1299. tests/events/test_snapshot.py +121 -0
  1300. tests/events/test_utils.py +986 -0
  1301. tests/federation/__init__.py +0 -0
  1302. tests/federation/test_complexity.py +257 -0
  1303. tests/federation/test_federation_catch_up.py +585 -0
  1304. tests/federation/test_federation_client.py +316 -0
  1305. tests/federation/test_federation_devices.py +161 -0
  1306. tests/federation/test_federation_media.py +295 -0
  1307. tests/federation/test_federation_out_of_band_membership.py +671 -0
  1308. tests/federation/test_federation_sender.py +956 -0
  1309. tests/federation/test_federation_server.py +631 -0
  1310. tests/federation/transport/__init__.py +0 -0
  1311. tests/federation/transport/server/__init__.py +20 -0
  1312. tests/federation/transport/server/test__base.py +156 -0
  1313. tests/federation/transport/test_client.py +157 -0
  1314. tests/federation/transport/test_knocking.py +323 -0
  1315. tests/federation/transport/test_server.py +74 -0
  1316. tests/handlers/__init__.py +0 -0
  1317. tests/handlers/oidc_test_key.p8 +5 -0
  1318. tests/handlers/oidc_test_key.pub.pem +4 -0
  1319. tests/handlers/test_admin.py +361 -0
  1320. tests/handlers/test_appservice.py +1336 -0
  1321. tests/handlers/test_auth.py +248 -0
  1322. tests/handlers/test_cas.py +239 -0
  1323. tests/handlers/test_deactivate_account.py +485 -0
  1324. tests/handlers/test_device.py +665 -0
  1325. tests/handlers/test_directory.py +613 -0
  1326. tests/handlers/test_e2e_keys.py +2025 -0
  1327. tests/handlers/test_e2e_room_keys.py +569 -0
  1328. tests/handlers/test_federation.py +794 -0
  1329. tests/handlers/test_federation_event.py +1181 -0
  1330. tests/handlers/test_message.py +322 -0
  1331. tests/handlers/test_oauth_delegation.py +1314 -0
  1332. tests/handlers/test_oidc.py +1688 -0
  1333. tests/handlers/test_password_providers.py +987 -0
  1334. tests/handlers/test_presence.py +2144 -0
  1335. tests/handlers/test_profile.py +401 -0
  1336. tests/handlers/test_receipts.py +342 -0
  1337. tests/handlers/test_register.py +880 -0
  1338. tests/handlers/test_room.py +108 -0
  1339. tests/handlers/test_room_list.py +93 -0
  1340. tests/handlers/test_room_member.py +764 -0
  1341. tests/handlers/test_room_policy.py +468 -0
  1342. tests/handlers/test_room_summary.py +1248 -0
  1343. tests/handlers/test_saml.py +427 -0
  1344. tests/handlers/test_send_email.py +230 -0
  1345. tests/handlers/test_sliding_sync.py +5065 -0
  1346. tests/handlers/test_sso.py +152 -0
  1347. tests/handlers/test_stats.py +594 -0
  1348. tests/handlers/test_sync.py +1275 -0
  1349. tests/handlers/test_typing.py +557 -0
  1350. tests/handlers/test_user_directory.py +1435 -0
  1351. tests/handlers/test_worker_lock.py +126 -0
  1352. tests/http/__init__.py +196 -0
  1353. tests/http/ca.crt +19 -0
  1354. tests/http/ca.key +27 -0
  1355. tests/http/federation/__init__.py +19 -0
  1356. tests/http/federation/test_matrix_federation_agent.py +1855 -0
  1357. tests/http/federation/test_srv_resolver.py +220 -0
  1358. tests/http/server/__init__.py +20 -0
  1359. tests/http/server/_base.py +621 -0
  1360. tests/http/server.key +27 -0
  1361. tests/http/test_additional_resource.py +76 -0
  1362. tests/http/test_client.py +422 -0
  1363. tests/http/test_endpoint.py +62 -0
  1364. tests/http/test_matrixfederationclient.py +1092 -0
  1365. tests/http/test_proxy.py +75 -0
  1366. tests/http/test_proxyagent.py +1008 -0
  1367. tests/http/test_servlet.py +145 -0
  1368. tests/http/test_simple_client.py +188 -0
  1369. tests/http/test_site.py +247 -0
  1370. tests/logging/__init__.py +42 -0
  1371. tests/logging/test_loggers.py +127 -0
  1372. tests/logging/test_opentracing.py +524 -0
  1373. tests/logging/test_remote_handler.py +184 -0
  1374. tests/logging/test_terse_json.py +253 -0
  1375. tests/media/__init__.py +20 -0
  1376. tests/media/test_base.py +88 -0
  1377. tests/media/test_filepath.py +602 -0
  1378. tests/media/test_html_preview.py +565 -0
  1379. tests/media/test_media_retention.py +299 -0
  1380. tests/media/test_media_storage.py +1401 -0
  1381. tests/media/test_oembed.py +172 -0
  1382. tests/media/test_url_previewer.py +120 -0
  1383. tests/metrics/__init__.py +0 -0
  1384. tests/metrics/test_background_process_metrics.py +21 -0
  1385. tests/metrics/test_metrics.py +407 -0
  1386. tests/metrics/test_phone_home_stats.py +263 -0
  1387. tests/module_api/__init__.py +0 -0
  1388. tests/module_api/test_account_data_manager.py +171 -0
  1389. tests/module_api/test_api.py +1035 -0
  1390. tests/module_api/test_event_unsigned_addition.py +66 -0
  1391. tests/module_api/test_spamchecker.py +286 -0
  1392. tests/push/__init__.py +0 -0
  1393. tests/push/test_bulk_push_rule_evaluator.py +652 -0
  1394. tests/push/test_email.py +570 -0
  1395. tests/push/test_http.py +1247 -0
  1396. tests/push/test_presentable_names.py +238 -0
  1397. tests/push/test_push_rule_evaluator.py +1069 -0
  1398. tests/replication/__init__.py +20 -0
  1399. tests/replication/_base.py +619 -0
  1400. tests/replication/http/__init__.py +20 -0
  1401. tests/replication/http/test__base.py +113 -0
  1402. tests/replication/storage/__init__.py +20 -0
  1403. tests/replication/storage/_base.py +85 -0
  1404. tests/replication/storage/test_events.py +299 -0
  1405. tests/replication/tcp/__init__.py +19 -0
  1406. tests/replication/tcp/streams/__init__.py +19 -0
  1407. tests/replication/tcp/streams/test_account_data.py +133 -0
  1408. tests/replication/tcp/streams/test_events.py +565 -0
  1409. tests/replication/tcp/streams/test_federation.py +117 -0
  1410. tests/replication/tcp/streams/test_partial_state.py +72 -0
  1411. tests/replication/tcp/streams/test_receipts.py +110 -0
  1412. tests/replication/tcp/streams/test_thread_subscriptions.py +157 -0
  1413. tests/replication/tcp/streams/test_to_device.py +112 -0
  1414. tests/replication/tcp/streams/test_typing.py +223 -0
  1415. tests/replication/tcp/test_commands.py +50 -0
  1416. tests/replication/tcp/test_handler.py +211 -0
  1417. tests/replication/test_auth.py +120 -0
  1418. tests/replication/test_client_reader_shard.py +101 -0
  1419. tests/replication/test_federation_ack.py +88 -0
  1420. tests/replication/test_federation_sender_shard.py +352 -0
  1421. tests/replication/test_module_cache_invalidation.py +89 -0
  1422. tests/replication/test_multi_media_repo.py +496 -0
  1423. tests/replication/test_pusher_shard.py +192 -0
  1424. tests/replication/test_sharded_event_persister.py +332 -0
  1425. tests/replication/test_sharded_receipts.py +250 -0
  1426. tests/rest/__init__.py +20 -0
  1427. tests/rest/admin/__init__.py +19 -0
  1428. tests/rest/admin/test_admin.py +614 -0
  1429. tests/rest/admin/test_background_updates.py +375 -0
  1430. tests/rest/admin/test_device.py +600 -0
  1431. tests/rest/admin/test_event.py +74 -0
  1432. tests/rest/admin/test_event_reports.py +781 -0
  1433. tests/rest/admin/test_federation.py +863 -0
  1434. tests/rest/admin/test_jwks.py +106 -0
  1435. tests/rest/admin/test_media.py +1091 -0
  1436. tests/rest/admin/test_registration_tokens.py +729 -0
  1437. tests/rest/admin/test_room.py +3626 -0
  1438. tests/rest/admin/test_scheduled_tasks.py +192 -0
  1439. tests/rest/admin/test_server_notice.py +753 -0
  1440. tests/rest/admin/test_statistics.py +523 -0
  1441. tests/rest/admin/test_user.py +6061 -0
  1442. tests/rest/admin/test_username_available.py +82 -0
  1443. tests/rest/client/__init__.py +20 -0
  1444. tests/rest/client/sliding_sync/__init__.py +13 -0
  1445. tests/rest/client/sliding_sync/test_connection_tracking.py +505 -0
  1446. tests/rest/client/sliding_sync/test_extension_account_data.py +1056 -0
  1447. tests/rest/client/sliding_sync/test_extension_e2ee.py +459 -0
  1448. tests/rest/client/sliding_sync/test_extension_receipts.py +934 -0
  1449. tests/rest/client/sliding_sync/test_extension_thread_subscriptions.py +497 -0
  1450. tests/rest/client/sliding_sync/test_extension_to_device.py +294 -0
  1451. tests/rest/client/sliding_sync/test_extension_typing.py +500 -0
  1452. tests/rest/client/sliding_sync/test_extensions.py +306 -0
  1453. tests/rest/client/sliding_sync/test_lists_filters.py +1975 -0
  1454. tests/rest/client/sliding_sync/test_room_subscriptions.py +303 -0
  1455. tests/rest/client/sliding_sync/test_rooms_invites.py +528 -0
  1456. tests/rest/client/sliding_sync/test_rooms_meta.py +1338 -0
  1457. tests/rest/client/sliding_sync/test_rooms_required_state.py +2247 -0
  1458. tests/rest/client/sliding_sync/test_rooms_timeline.py +718 -0
  1459. tests/rest/client/sliding_sync/test_sliding_sync.py +1688 -0
  1460. tests/rest/client/test_account.py +1543 -0
  1461. tests/rest/client/test_account_data.py +81 -0
  1462. tests/rest/client/test_auth.py +1508 -0
  1463. tests/rest/client/test_auth_metadata.py +145 -0
  1464. tests/rest/client/test_capabilities.py +318 -0
  1465. tests/rest/client/test_consent.py +138 -0
  1466. tests/rest/client/test_delayed_events.py +553 -0
  1467. tests/rest/client/test_devices.py +634 -0
  1468. tests/rest/client/test_directory.py +249 -0
  1469. tests/rest/client/test_ephemeral_message.py +113 -0
  1470. tests/rest/client/test_events.py +165 -0
  1471. tests/rest/client/test_filter.py +124 -0
  1472. tests/rest/client/test_identity.py +67 -0
  1473. tests/rest/client/test_keys.py +516 -0
  1474. tests/rest/client/test_login.py +1881 -0
  1475. tests/rest/client/test_login_token_request.py +175 -0
  1476. tests/rest/client/test_matrixrtc.py +105 -0
  1477. tests/rest/client/test_media.py +3156 -0
  1478. tests/rest/client/test_models.py +83 -0
  1479. tests/rest/client/test_mutual_rooms.py +235 -0
  1480. tests/rest/client/test_notifications.py +231 -0
  1481. tests/rest/client/test_owned_state.py +308 -0
  1482. tests/rest/client/test_password_policy.py +186 -0
  1483. tests/rest/client/test_power_levels.py +295 -0
  1484. tests/rest/client/test_presence.py +149 -0
  1485. tests/rest/client/test_profile.py +925 -0
  1486. tests/rest/client/test_push_rule_attrs.py +510 -0
  1487. tests/rest/client/test_read_marker.py +151 -0
  1488. tests/rest/client/test_receipts.py +287 -0
  1489. tests/rest/client/test_redactions.py +657 -0
  1490. tests/rest/client/test_register.py +1314 -0
  1491. tests/rest/client/test_relations.py +1954 -0
  1492. tests/rest/client/test_rendezvous.py +468 -0
  1493. tests/rest/client/test_reporting.py +324 -0
  1494. tests/rest/client/test_retention.py +389 -0
  1495. tests/rest/client/test_rooms.py +5486 -0
  1496. tests/rest/client/test_sendtodevice.py +271 -0
  1497. tests/rest/client/test_shadow_banned.py +335 -0
  1498. tests/rest/client/test_sync.py +1147 -0
  1499. tests/rest/client/test_tags.py +161 -0
  1500. tests/rest/client/test_third_party_rules.py +1076 -0
  1501. tests/rest/client/test_thread_subscriptions.py +351 -0
  1502. tests/rest/client/test_transactions.py +204 -0
  1503. tests/rest/client/test_typing.py +114 -0
  1504. tests/rest/client/test_upgrade_room.py +433 -0
  1505. tests/rest/client/utils.py +985 -0
  1506. tests/rest/key/__init__.py +0 -0
  1507. tests/rest/key/v2/__init__.py +0 -0
  1508. tests/rest/key/v2/test_remote_key_resource.py +282 -0
  1509. tests/rest/media/__init__.py +19 -0
  1510. tests/rest/media/test_domain_blocking.py +148 -0
  1511. tests/rest/media/test_url_preview.py +1445 -0
  1512. tests/rest/synapse/__init__.py +12 -0
  1513. tests/rest/synapse/client/__init__.py +12 -0
  1514. tests/rest/synapse/client/test_federation_whitelist.py +118 -0
  1515. tests/rest/synapse/mas/__init__.py +12 -0
  1516. tests/rest/synapse/mas/_base.py +43 -0
  1517. tests/rest/synapse/mas/test_devices.py +693 -0
  1518. tests/rest/synapse/mas/test_users.py +1399 -0
  1519. tests/rest/test_health.py +35 -0
  1520. tests/rest/test_well_known.py +155 -0
  1521. tests/scripts/__init__.py +0 -0
  1522. tests/scripts/test_new_matrix_user.py +172 -0
  1523. tests/server.py +1374 -0
  1524. tests/server_notices/__init__.py +241 -0
  1525. tests/server_notices/test_consent.py +111 -0
  1526. tests/server_notices/test_resource_limits_server_notices.py +409 -0
  1527. tests/state/__init__.py +0 -0
  1528. tests/state/test_v2.py +1096 -0
  1529. tests/state/test_v21.py +506 -0
  1530. tests/storage/__init__.py +0 -0
  1531. tests/storage/databases/__init__.py +20 -0
  1532. tests/storage/databases/main/__init__.py +20 -0
  1533. tests/storage/databases/main/test_cache.py +124 -0
  1534. tests/storage/databases/main/test_deviceinbox.py +323 -0
  1535. tests/storage/databases/main/test_end_to_end_keys.py +127 -0
  1536. tests/storage/databases/main/test_events_worker.py +594 -0
  1537. tests/storage/databases/main/test_lock.py +499 -0
  1538. tests/storage/databases/main/test_metrics.py +88 -0
  1539. tests/storage/databases/main/test_receipts.py +218 -0
  1540. tests/storage/databases/main/test_room.py +192 -0
  1541. tests/storage/test__base.py +178 -0
  1542. tests/storage/test_account_data.py +186 -0
  1543. tests/storage/test_appservice.py +568 -0
  1544. tests/storage/test_background_update.py +671 -0
  1545. tests/storage/test_base.py +813 -0
  1546. tests/storage/test_cleanup_extrems.py +396 -0
  1547. tests/storage/test_client_ips.py +788 -0
  1548. tests/storage/test_database.py +288 -0
  1549. tests/storage/test_devices.py +353 -0
  1550. tests/storage/test_directory.py +74 -0
  1551. tests/storage/test_e2e_room_keys.py +87 -0
  1552. tests/storage/test_end_to_end_keys.py +120 -0
  1553. tests/storage/test_event_chain.py +826 -0
  1554. tests/storage/test_event_federation.py +1433 -0
  1555. tests/storage/test_event_push_actions.py +809 -0
  1556. tests/storage/test_events.py +591 -0
  1557. tests/storage/test_events_bg_updates.py +156 -0
  1558. tests/storage/test_id_generators.py +791 -0
  1559. tests/storage/test_invite_rule.py +171 -0
  1560. tests/storage/test_main.py +56 -0
  1561. tests/storage/test_monthly_active_users.py +500 -0
  1562. tests/storage/test_profile.py +134 -0
  1563. tests/storage/test_purge.py +459 -0
  1564. tests/storage/test_receipts.py +309 -0
  1565. tests/storage/test_redaction.py +462 -0
  1566. tests/storage/test_registration.py +277 -0
  1567. tests/storage/test_relations.py +118 -0
  1568. tests/storage/test_rollback_worker.py +132 -0
  1569. tests/storage/test_room.py +69 -0
  1570. tests/storage/test_room_search.py +383 -0
  1571. tests/storage/test_roommember.py +812 -0
  1572. tests/storage/test_sliding_sync_tables.py +5187 -0
  1573. tests/storage/test_state.py +959 -0
  1574. tests/storage/test_state_deletion.py +475 -0
  1575. tests/storage/test_stream.py +1533 -0
  1576. tests/storage/test_thread_subscriptions.py +369 -0
  1577. tests/storage/test_transactions.py +77 -0
  1578. tests/storage/test_txn_limit.py +49 -0
  1579. tests/storage/test_unsafe_locale.py +67 -0
  1580. tests/storage/test_user_directory.py +691 -0
  1581. tests/storage/test_user_filters.py +101 -0
  1582. tests/storage/util/__init__.py +20 -0
  1583. tests/storage/util/test_partial_state_events_tracker.py +181 -0
  1584. tests/synapse_rust/__init__.py +11 -0
  1585. tests/synapse_rust/test_http_client.py +225 -0
  1586. tests/test_distributor.py +74 -0
  1587. tests/test_event_auth.py +921 -0
  1588. tests/test_mau.py +347 -0
  1589. tests/test_phone_home.py +102 -0
  1590. tests/test_rust.py +11 -0
  1591. tests/test_server.py +557 -0
  1592. tests/test_state.py +902 -0
  1593. tests/test_terms_auth.py +128 -0
  1594. tests/test_types.py +201 -0
  1595. tests/test_utils/__init__.py +161 -0
  1596. tests/test_utils/event_injection.py +150 -0
  1597. tests/test_utils/html_parsers.py +59 -0
  1598. tests/test_utils/logging_setup.py +74 -0
  1599. tests/test_utils/oidc.py +370 -0
  1600. tests/test_visibility.py +712 -0
  1601. tests/types/__init__.py +0 -0
  1602. tests/types/test_init.py +51 -0
  1603. tests/types/test_state.py +627 -0
  1604. tests/unittest.py +1108 -0
  1605. tests/util/__init__.py +20 -0
  1606. tests/util/caches/__init__.py +20 -0
  1607. tests/util/caches/test_cached_call.py +168 -0
  1608. tests/util/caches/test_deferred_cache.py +317 -0
  1609. tests/util/caches/test_descriptors.py +1110 -0
  1610. tests/util/caches/test_response_cache.py +225 -0
  1611. tests/util/caches/test_ttlcache.py +90 -0
  1612. tests/util/test_async_helpers.py +808 -0
  1613. tests/util/test_background_queue.py +117 -0
  1614. tests/util/test_batching_queue.py +252 -0
  1615. tests/util/test_check_dependencies.py +243 -0
  1616. tests/util/test_dict_cache.py +130 -0
  1617. tests/util/test_events.py +118 -0
  1618. tests/util/test_expiring_cache.py +113 -0
  1619. tests/util/test_file_consumer.py +199 -0
  1620. tests/util/test_itertools.py +190 -0
  1621. tests/util/test_linearizer.py +264 -0
  1622. tests/util/test_logcontext.py +715 -0
  1623. tests/util/test_logformatter.py +44 -0
  1624. tests/util/test_lrucache.py +479 -0
  1625. tests/util/test_macaroons.py +126 -0
  1626. tests/util/test_mutable_overlay_mapping.py +189 -0
  1627. tests/util/test_ratelimitutils.py +146 -0
  1628. tests/util/test_retryutils.py +314 -0
  1629. tests/util/test_rwlock.py +401 -0
  1630. tests/util/test_stream_change_cache.py +304 -0
  1631. tests/util/test_stringutils.py +86 -0
  1632. tests/util/test_task_scheduler.py +227 -0
  1633. tests/util/test_threepids.py +55 -0
  1634. tests/util/test_treecache.py +93 -0
  1635. tests/util/test_wheel_timer.py +82 -0
  1636. tests/utils.py +342 -0
@@ -0,0 +1,3156 @@
1
+ #
2
+ # This file is licensed under the Affero General Public License (AGPL) version 3.
3
+ #
4
+ # Copyright 2022 The Matrix.org Foundation C.I.C.
5
+ # Copyright (C) 2024 New Vector, Ltd
6
+ #
7
+ # This program is free software: you can redistribute it and/or modify
8
+ # it under the terms of the GNU Affero General Public License as
9
+ # published by the Free Software Foundation, either version 3 of the
10
+ # License, or (at your option) any later version.
11
+ #
12
+ # See the GNU Affero General Public License for more details:
13
+ # <https://www.gnu.org/licenses/agpl-3.0.html>.
14
+ #
15
+ # Originally licensed under the Apache License, Version 2.0:
16
+ # <http://www.apache.org/licenses/LICENSE-2.0>.
17
+ #
18
+ # [This file includes modifications made by New Vector Limited]
19
+ #
20
+ #
21
+ import base64
22
+ import io
23
+ import json
24
+ import os
25
+ import re
26
+ import shutil
27
+ from typing import Any, BinaryIO, ClassVar, Sequence
28
+ from unittest.mock import MagicMock, Mock, patch
29
+ from urllib import parse
30
+ from urllib.parse import quote, urlencode
31
+
32
+ from parameterized import parameterized, parameterized_class
33
+ from PIL import Image as Image
34
+
35
+ from twisted.internet import defer
36
+ from twisted.internet._resolver import HostResolution
37
+ from twisted.internet.address import IPv4Address, IPv6Address
38
+ from twisted.internet.defer import Deferred
39
+ from twisted.internet.error import DNSLookupError
40
+ from twisted.internet.interfaces import IAddress, IResolutionReceiver
41
+ from twisted.internet.testing import AccumulatingProtocol, MemoryReactor
42
+ from twisted.python.failure import Failure
43
+ from twisted.web.http_headers import Headers
44
+ from twisted.web.iweb import UNKNOWN_LENGTH, IResponse
45
+ from twisted.web.resource import Resource
46
+
47
+ from synapse.api.errors import HttpResponseException
48
+ from synapse.api.ratelimiting import Ratelimiter
49
+ from synapse.config._base import Config
50
+ from synapse.config.oembed import OEmbedEndpointConfig
51
+ from synapse.http.client import MultipartResponse
52
+ from synapse.http.types import QueryParams
53
+ from synapse.logging.context import make_deferred_yieldable
54
+ from synapse.media._base import FileInfo, ThumbnailInfo
55
+ from synapse.media.thumbnailer import ThumbnailProvider
56
+ from synapse.media.url_previewer import IMAGE_CACHE_EXPIRY_MS
57
+ from synapse.module_api import MediaUploadLimit
58
+ from synapse.rest import admin
59
+ from synapse.rest.client import login, media
60
+ from synapse.server import HomeServer
61
+ from synapse.types import JsonDict, UserID
62
+ from synapse.util.clock import Clock
63
+ from synapse.util.stringutils import parse_and_validate_mxc_uri
64
+
65
+ from tests import unittest
66
+ from tests.media.test_media_storage import (
67
+ SVG,
68
+ TestImage,
69
+ empty_file,
70
+ small_cmyk_jpeg,
71
+ small_lossless_webp,
72
+ small_png,
73
+ small_png_with_transparency,
74
+ )
75
+ from tests.server import FakeChannel, FakeTransport, ThreadedMemoryReactorClock
76
+ from tests.test_utils import SMALL_PNG
77
+ from tests.unittest import override_config
78
+
79
+ try:
80
+ import lxml
81
+ except ImportError:
82
+ lxml = None # type: ignore[assignment]
83
+
84
+
85
+ class MediaDomainBlockingTests(unittest.HomeserverTestCase):
86
+ remote_media_id = "doesnotmatter"
87
+ remote_server_name = "evil.com"
88
+ servlets = [
89
+ media.register_servlets,
90
+ admin.register_servlets,
91
+ login.register_servlets,
92
+ ]
93
+
94
+ def make_homeserver(
95
+ self, reactor: ThreadedMemoryReactorClock, clock: Clock
96
+ ) -> HomeServer:
97
+ config = self.default_config()
98
+
99
+ self.storage_path = self.mktemp()
100
+ self.media_store_path = self.mktemp()
101
+ os.mkdir(self.storage_path)
102
+ os.mkdir(self.media_store_path)
103
+ config["media_store_path"] = self.media_store_path
104
+
105
+ provider_config = {
106
+ "module": "synapse.media.storage_provider.FileStorageProviderBackend",
107
+ "store_local": True,
108
+ "store_synchronous": False,
109
+ "store_remote": True,
110
+ "config": {"directory": self.storage_path},
111
+ }
112
+
113
+ config["media_storage_providers"] = [provider_config]
114
+
115
+ return self.setup_test_homeserver(config=config)
116
+
117
+ def prepare(self, reactor: MemoryReactor, clock: Clock, hs: HomeServer) -> None:
118
+ self.store = hs.get_datastores().main
119
+
120
+ # Inject a piece of media. We'll use this to ensure we're returning a sane
121
+ # response when we're not supposed to block it, distinguishing a media block
122
+ # from a regular 404.
123
+ file_id = "abcdefg12345"
124
+ file_info = FileInfo(server_name=self.remote_server_name, file_id=file_id)
125
+
126
+ media_storage = hs.get_media_repository().media_storage
127
+
128
+ ctx = media_storage.store_into_file(file_info)
129
+ (f, fname) = self.get_success(ctx.__aenter__())
130
+ f.write(SMALL_PNG)
131
+ self.get_success(ctx.__aexit__(None, None, None))
132
+
133
+ self.get_success(
134
+ self.store.store_cached_remote_media(
135
+ origin=self.remote_server_name,
136
+ media_id=self.remote_media_id,
137
+ media_type="image/png",
138
+ media_length=1,
139
+ time_now_ms=clock.time_msec(),
140
+ upload_name="test.png",
141
+ filesystem_id=file_id,
142
+ sha256=file_id,
143
+ )
144
+ )
145
+ self.register_user("user", "password")
146
+ self.tok = self.login("user", "password")
147
+
148
+ @override_config(
149
+ {
150
+ # Disable downloads from the domain we'll be trying to download from.
151
+ # Should result in a 404.
152
+ "prevent_media_downloads_from": ["evil.com"],
153
+ "dynamic_thumbnails": True,
154
+ }
155
+ )
156
+ def test_cannot_download_blocked_media_thumbnail(self) -> None:
157
+ """
158
+ Same test as test_cannot_download_blocked_media but for thumbnails.
159
+ """
160
+ response = self.make_request(
161
+ "GET",
162
+ f"/_matrix/client/v1/media/thumbnail/evil.com/{self.remote_media_id}?width=100&height=100",
163
+ shorthand=False,
164
+ content={"width": 100, "height": 100},
165
+ access_token=self.tok,
166
+ )
167
+ self.assertEqual(response.code, 404)
168
+
169
+ @override_config(
170
+ {
171
+ # Disable downloads from a domain we won't be requesting downloads from.
172
+ # This proves we haven't broken anything.
173
+ "prevent_media_downloads_from": ["not-listed.com"],
174
+ "dynamic_thumbnails": True,
175
+ }
176
+ )
177
+ def test_remote_media_thumbnail_normally_unblocked(self) -> None:
178
+ """
179
+ Same test as test_remote_media_normally_unblocked but for thumbnails.
180
+ """
181
+ response = self.make_request(
182
+ "GET",
183
+ f"/_matrix/client/v1/media/thumbnail/evil.com/{self.remote_media_id}?width=100&height=100",
184
+ shorthand=False,
185
+ access_token=self.tok,
186
+ )
187
+ self.assertEqual(response.code, 200)
188
+
189
+
190
+ class URLPreviewTests(unittest.HomeserverTestCase):
191
+ if not lxml:
192
+ skip = "url preview feature requires lxml"
193
+
194
+ servlets = [media.register_servlets]
195
+ hijack_auth = True
196
+ user_id = "@test:user"
197
+ end_content = (
198
+ b"<html><head>"
199
+ b'<meta property="og:title" content="~matrix~" />'
200
+ b'<meta property="og:description" content="hi" />'
201
+ b"</head></html>"
202
+ )
203
+
204
+ def make_homeserver(self, reactor: MemoryReactor, clock: Clock) -> HomeServer:
205
+ config = self.default_config()
206
+ config["url_preview_enabled"] = True
207
+ config["max_spider_size"] = 9999999
208
+ config["url_preview_ip_range_blacklist"] = (
209
+ "192.168.1.1",
210
+ "1.0.0.0/8",
211
+ "3fff:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
212
+ "2001:800::/21",
213
+ )
214
+ config["url_preview_ip_range_whitelist"] = ("1.1.1.1",)
215
+ config["url_preview_accept_language"] = [
216
+ "en-UK",
217
+ "en-US;q=0.9",
218
+ "fr;q=0.8",
219
+ "*;q=0.7",
220
+ ]
221
+
222
+ self.storage_path = self.mktemp()
223
+ self.media_store_path = self.mktemp()
224
+ os.mkdir(self.storage_path)
225
+ os.mkdir(self.media_store_path)
226
+ config["media_store_path"] = self.media_store_path
227
+
228
+ provider_config = {
229
+ "module": "synapse.media.storage_provider.FileStorageProviderBackend",
230
+ "store_local": True,
231
+ "store_synchronous": False,
232
+ "store_remote": True,
233
+ "config": {"directory": self.storage_path},
234
+ }
235
+
236
+ config["media_storage_providers"] = [provider_config]
237
+
238
+ hs = self.setup_test_homeserver(config=config)
239
+
240
+ # After the hs is created, modify the parsed oEmbed config (to avoid
241
+ # messing with files).
242
+ #
243
+ # Note that HTTP URLs are used to avoid having to deal with TLS in tests.
244
+ hs.config.oembed.oembed_patterns = [
245
+ OEmbedEndpointConfig(
246
+ api_endpoint="http://publish.twitter.com/oembed",
247
+ url_patterns=[
248
+ re.compile(r"http://twitter\.com/.+/status/.+"),
249
+ ],
250
+ formats=None,
251
+ ),
252
+ OEmbedEndpointConfig(
253
+ api_endpoint="http://www.hulu.com/api/oembed.{format}",
254
+ url_patterns=[
255
+ re.compile(r"http://www\.hulu\.com/watch/.+"),
256
+ ],
257
+ formats=["json"],
258
+ ),
259
+ ]
260
+
261
+ return hs
262
+
263
+ def prepare(self, reactor: MemoryReactor, clock: Clock, hs: HomeServer) -> None:
264
+ self.media_repo = hs.get_media_repository()
265
+ assert self.media_repo.url_previewer is not None
266
+ self.url_previewer = self.media_repo.url_previewer
267
+
268
+ self.lookups: dict[str, Any] = {}
269
+
270
+ class Resolver:
271
+ def resolveHostName(
272
+ _self,
273
+ resolutionReceiver: IResolutionReceiver,
274
+ hostName: str,
275
+ portNumber: int = 0,
276
+ addressTypes: Sequence[type[IAddress]] | None = None,
277
+ transportSemantics: str = "TCP",
278
+ ) -> IResolutionReceiver:
279
+ resolution = HostResolution(hostName)
280
+ resolutionReceiver.resolutionBegan(resolution)
281
+ if hostName not in self.lookups:
282
+ raise DNSLookupError("OH NO")
283
+
284
+ for i in self.lookups[hostName]:
285
+ resolutionReceiver.addressResolved(i[0]("TCP", i[1], portNumber))
286
+ resolutionReceiver.resolutionComplete()
287
+ return resolutionReceiver
288
+
289
+ self.reactor.nameResolver = Resolver() # type: ignore[assignment]
290
+
291
+ def _assert_small_png(self, json_body: JsonDict) -> None:
292
+ """Assert properties from the SMALL_PNG test image."""
293
+ self.assertTrue(json_body["og:image"].startswith("mxc://"))
294
+ self.assertEqual(json_body["og:image:height"], 1)
295
+ self.assertEqual(json_body["og:image:width"], 1)
296
+ self.assertEqual(json_body["og:image:type"], "image/png")
297
+ self.assertEqual(json_body["matrix:image:size"], 67)
298
+
299
+ def test_cache_returns_correct_type(self) -> None:
300
+ self.lookups["matrix.org"] = [(IPv4Address, "10.1.2.3")]
301
+
302
+ channel = self.make_request(
303
+ "GET",
304
+ "/_matrix/client/v1/media/preview_url?url=http://matrix.org",
305
+ shorthand=False,
306
+ await_result=False,
307
+ )
308
+ self.pump()
309
+
310
+ client = self.reactor.tcpClients[0][2].buildProtocol(None)
311
+ server = AccumulatingProtocol()
312
+ server.makeConnection(FakeTransport(client, self.reactor))
313
+ client.makeConnection(FakeTransport(server, self.reactor))
314
+ client.dataReceived(
315
+ b"HTTP/1.0 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n"
316
+ % (len(self.end_content),)
317
+ + self.end_content
318
+ )
319
+
320
+ self.pump()
321
+ self.assertEqual(channel.code, 200)
322
+ self.assertEqual(
323
+ channel.json_body, {"og:title": "~matrix~", "og:description": "hi"}
324
+ )
325
+
326
+ # Check the cache returns the correct response
327
+ channel = self.make_request(
328
+ "GET",
329
+ "/_matrix/client/v1/media/preview_url?url=http://matrix.org",
330
+ shorthand=False,
331
+ )
332
+
333
+ # Check the cache response has the same content
334
+ self.assertEqual(channel.code, 200)
335
+ self.assertEqual(
336
+ channel.json_body, {"og:title": "~matrix~", "og:description": "hi"}
337
+ )
338
+
339
+ # Clear the in-memory cache
340
+ self.assertIn("http://matrix.org", self.url_previewer._cache)
341
+ self.url_previewer._cache.pop("http://matrix.org")
342
+ self.assertNotIn("http://matrix.org", self.url_previewer._cache)
343
+
344
+ # Check the database cache returns the correct response
345
+ channel = self.make_request(
346
+ "GET",
347
+ "/_matrix/client/v1/media/preview_url?url=http://matrix.org",
348
+ shorthand=False,
349
+ )
350
+
351
+ # Check the cache response has the same content
352
+ self.assertEqual(channel.code, 200)
353
+ self.assertEqual(
354
+ channel.json_body, {"og:title": "~matrix~", "og:description": "hi"}
355
+ )
356
+
357
+ def test_non_ascii_preview_httpequiv(self) -> None:
358
+ self.lookups["matrix.org"] = [(IPv4Address, "10.1.2.3")]
359
+
360
+ end_content = (
361
+ b"<html><head>"
362
+ b'<meta http-equiv="Content-Type" content="text/html; charset=windows-1251"/>'
363
+ b'<meta property="og:title" content="\xe4\xea\xe0" />'
364
+ b'<meta property="og:description" content="hi" />'
365
+ b"</head></html>"
366
+ )
367
+
368
+ channel = self.make_request(
369
+ "GET",
370
+ "/_matrix/client/v1/media/preview_url?url=http://matrix.org",
371
+ shorthand=False,
372
+ await_result=False,
373
+ )
374
+ self.pump()
375
+
376
+ client = self.reactor.tcpClients[0][2].buildProtocol(None)
377
+ server = AccumulatingProtocol()
378
+ server.makeConnection(FakeTransport(client, self.reactor))
379
+ client.makeConnection(FakeTransport(server, self.reactor))
380
+ client.dataReceived(
381
+ (
382
+ b"HTTP/1.0 200 OK\r\nContent-Length: %d\r\n"
383
+ b'Content-Type: text/html; charset="utf8"\r\n\r\n'
384
+ )
385
+ % (len(end_content),)
386
+ + end_content
387
+ )
388
+
389
+ self.pump()
390
+ self.assertEqual(channel.code, 200)
391
+ self.assertEqual(channel.json_body["og:title"], "\u0434\u043a\u0430")
392
+
393
+ def test_video_rejected(self) -> None:
394
+ self.lookups["matrix.org"] = [(IPv4Address, "10.1.2.3")]
395
+
396
+ end_content = b"anything"
397
+
398
+ channel = self.make_request(
399
+ "GET",
400
+ "/_matrix/client/v1/media/preview_url?url=http://matrix.org",
401
+ shorthand=False,
402
+ await_result=False,
403
+ )
404
+ self.pump()
405
+
406
+ client = self.reactor.tcpClients[0][2].buildProtocol(None)
407
+ server = AccumulatingProtocol()
408
+ server.makeConnection(FakeTransport(client, self.reactor))
409
+ client.makeConnection(FakeTransport(server, self.reactor))
410
+ client.dataReceived(
411
+ (
412
+ b"HTTP/1.0 200 OK\r\nContent-Length: %d\r\n"
413
+ b"Content-Type: video/mp4\r\n\r\n"
414
+ )
415
+ % (len(end_content))
416
+ + end_content
417
+ )
418
+
419
+ self.pump()
420
+ self.assertEqual(channel.code, 502)
421
+ self.assertEqual(
422
+ channel.json_body,
423
+ {
424
+ "errcode": "M_UNKNOWN",
425
+ "error": "Requested file's content type not allowed for this operation: video/mp4",
426
+ },
427
+ )
428
+
429
+ def test_audio_rejected(self) -> None:
430
+ self.lookups["matrix.org"] = [(IPv4Address, "10.1.2.3")]
431
+
432
+ end_content = b"anything"
433
+
434
+ channel = self.make_request(
435
+ "GET",
436
+ "/_matrix/client/v1/media/preview_url?url=http://matrix.org",
437
+ shorthand=False,
438
+ await_result=False,
439
+ )
440
+ self.pump()
441
+
442
+ client = self.reactor.tcpClients[0][2].buildProtocol(None)
443
+ server = AccumulatingProtocol()
444
+ server.makeConnection(FakeTransport(client, self.reactor))
445
+ client.makeConnection(FakeTransport(server, self.reactor))
446
+ client.dataReceived(
447
+ (
448
+ b"HTTP/1.0 200 OK\r\nContent-Length: %d\r\n"
449
+ b"Content-Type: audio/aac\r\n\r\n"
450
+ )
451
+ % (len(end_content))
452
+ + end_content
453
+ )
454
+
455
+ self.pump()
456
+ self.assertEqual(channel.code, 502)
457
+ self.assertEqual(
458
+ channel.json_body,
459
+ {
460
+ "errcode": "M_UNKNOWN",
461
+ "error": "Requested file's content type not allowed for this operation: audio/aac",
462
+ },
463
+ )
464
+
465
+ def test_non_ascii_preview_content_type(self) -> None:
466
+ self.lookups["matrix.org"] = [(IPv4Address, "10.1.2.3")]
467
+
468
+ end_content = (
469
+ b"<html><head>"
470
+ b'<meta property="og:title" content="\xe4\xea\xe0" />'
471
+ b'<meta property="og:description" content="hi" />'
472
+ b"</head></html>"
473
+ )
474
+
475
+ channel = self.make_request(
476
+ "GET",
477
+ "/_matrix/client/v1/media/preview_url?url=http://matrix.org",
478
+ shorthand=False,
479
+ await_result=False,
480
+ )
481
+ self.pump()
482
+
483
+ client = self.reactor.tcpClients[0][2].buildProtocol(None)
484
+ server = AccumulatingProtocol()
485
+ server.makeConnection(FakeTransport(client, self.reactor))
486
+ client.makeConnection(FakeTransport(server, self.reactor))
487
+ client.dataReceived(
488
+ (
489
+ b"HTTP/1.0 200 OK\r\nContent-Length: %d\r\n"
490
+ b'Content-Type: text/html; charset="windows-1251"\r\n\r\n'
491
+ )
492
+ % (len(end_content),)
493
+ + end_content
494
+ )
495
+
496
+ self.pump()
497
+ self.assertEqual(channel.code, 200)
498
+ self.assertEqual(channel.json_body["og:title"], "\u0434\u043a\u0430")
499
+
500
+ def test_overlong_title(self) -> None:
501
+ self.lookups["matrix.org"] = [(IPv4Address, "10.1.2.3")]
502
+
503
+ end_content = (
504
+ b"<html><head>"
505
+ b"<title>" + b"x" * 2000 + b"</title>"
506
+ b'<meta property="og:description" content="hi" />'
507
+ b"</head></html>"
508
+ )
509
+
510
+ channel = self.make_request(
511
+ "GET",
512
+ "/_matrix/client/v1/media/preview_url?url=http://matrix.org",
513
+ shorthand=False,
514
+ await_result=False,
515
+ )
516
+ self.pump()
517
+
518
+ client = self.reactor.tcpClients[0][2].buildProtocol(None)
519
+ server = AccumulatingProtocol()
520
+ server.makeConnection(FakeTransport(client, self.reactor))
521
+ client.makeConnection(FakeTransport(server, self.reactor))
522
+ client.dataReceived(
523
+ (
524
+ b"HTTP/1.0 200 OK\r\nContent-Length: %d\r\n"
525
+ b'Content-Type: text/html; charset="windows-1251"\r\n\r\n'
526
+ )
527
+ % (len(end_content),)
528
+ + end_content
529
+ )
530
+
531
+ self.pump()
532
+ self.assertEqual(channel.code, 200)
533
+ res = channel.json_body
534
+ # We should only see the `og:description` field, as `title` is too long and should be stripped out
535
+ self.assertCountEqual(["og:description"], res.keys())
536
+
537
+ def test_ipaddr(self) -> None:
538
+ """
539
+ IP addresses can be previewed directly.
540
+ """
541
+ self.lookups["example.com"] = [(IPv4Address, "10.1.2.3")]
542
+
543
+ channel = self.make_request(
544
+ "GET",
545
+ "/_matrix/client/v1/media/preview_url?url=http://example.com",
546
+ shorthand=False,
547
+ await_result=False,
548
+ )
549
+ self.pump()
550
+
551
+ client = self.reactor.tcpClients[0][2].buildProtocol(None)
552
+ server = AccumulatingProtocol()
553
+ server.makeConnection(FakeTransport(client, self.reactor))
554
+ client.makeConnection(FakeTransport(server, self.reactor))
555
+ client.dataReceived(
556
+ b"HTTP/1.0 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n"
557
+ % (len(self.end_content),)
558
+ + self.end_content
559
+ )
560
+
561
+ self.pump()
562
+ self.assertEqual(channel.code, 200)
563
+ self.assertEqual(
564
+ channel.json_body, {"og:title": "~matrix~", "og:description": "hi"}
565
+ )
566
+
567
+ def test_blocked_ip_specific(self) -> None:
568
+ """
569
+ Blocked IP addresses, found via DNS, are not spidered.
570
+ """
571
+ self.lookups["example.com"] = [(IPv4Address, "192.168.1.1")]
572
+
573
+ channel = self.make_request(
574
+ "GET",
575
+ "/_matrix/client/v1/media/preview_url?url=http://example.com",
576
+ shorthand=False,
577
+ )
578
+
579
+ # No requests made.
580
+ self.assertEqual(len(self.reactor.tcpClients), 0)
581
+ self.assertEqual(channel.code, 502)
582
+ self.assertEqual(
583
+ channel.json_body,
584
+ {
585
+ "errcode": "M_UNKNOWN",
586
+ "error": "DNS resolution failure during URL preview generation",
587
+ },
588
+ )
589
+
590
+ def test_blocked_ip_range(self) -> None:
591
+ """
592
+ Blocked IP ranges, IPs found over DNS, are not spidered.
593
+ """
594
+ self.lookups["example.com"] = [(IPv4Address, "1.1.1.2")]
595
+
596
+ channel = self.make_request(
597
+ "GET",
598
+ "/_matrix/client/v1/media/preview_url?url=http://example.com",
599
+ shorthand=False,
600
+ )
601
+
602
+ self.assertEqual(channel.code, 502)
603
+ self.assertEqual(
604
+ channel.json_body,
605
+ {
606
+ "errcode": "M_UNKNOWN",
607
+ "error": "DNS resolution failure during URL preview generation",
608
+ },
609
+ )
610
+
611
+ def test_blocked_ip_specific_direct(self) -> None:
612
+ """
613
+ Blocked IP addresses, accessed directly, are not spidered.
614
+ """
615
+ channel = self.make_request(
616
+ "GET",
617
+ "/_matrix/client/v1/media/preview_url?url=http://192.168.1.1",
618
+ shorthand=False,
619
+ )
620
+
621
+ # No requests made.
622
+ self.assertEqual(len(self.reactor.tcpClients), 0)
623
+ self.assertEqual(
624
+ channel.json_body,
625
+ {"errcode": "M_UNKNOWN", "error": "IP address blocked"},
626
+ )
627
+ self.assertEqual(channel.code, 403)
628
+
629
+ def test_blocked_ip_range_direct(self) -> None:
630
+ """
631
+ Blocked IP ranges, accessed directly, are not spidered.
632
+ """
633
+ channel = self.make_request(
634
+ "GET",
635
+ "/_matrix/client/v1/media/preview_url?url=http://1.1.1.2",
636
+ shorthand=False,
637
+ )
638
+
639
+ self.assertEqual(channel.code, 403)
640
+ self.assertEqual(
641
+ channel.json_body,
642
+ {"errcode": "M_UNKNOWN", "error": "IP address blocked"},
643
+ )
644
+
645
+ def test_blocked_ip_range_whitelisted_ip(self) -> None:
646
+ """
647
+ Blocked but then subsequently whitelisted IP addresses can be
648
+ spidered.
649
+ """
650
+ self.lookups["example.com"] = [(IPv4Address, "1.1.1.1")]
651
+
652
+ channel = self.make_request(
653
+ "GET",
654
+ "/_matrix/client/v1/media/preview_url?url=http://example.com",
655
+ shorthand=False,
656
+ await_result=False,
657
+ )
658
+ self.pump()
659
+
660
+ client = self.reactor.tcpClients[0][2].buildProtocol(None)
661
+
662
+ server = AccumulatingProtocol()
663
+ server.makeConnection(FakeTransport(client, self.reactor))
664
+ client.makeConnection(FakeTransport(server, self.reactor))
665
+
666
+ client.dataReceived(
667
+ b"HTTP/1.0 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n"
668
+ % (len(self.end_content),)
669
+ + self.end_content
670
+ )
671
+
672
+ self.pump()
673
+ self.assertEqual(channel.code, 200)
674
+ self.assertEqual(
675
+ channel.json_body, {"og:title": "~matrix~", "og:description": "hi"}
676
+ )
677
+
678
+ def test_blocked_ip_with_external_ip(self) -> None:
679
+ """
680
+ If a hostname resolves a blocked IP, even if there's a non-blocked one,
681
+ it will be rejected.
682
+ """
683
+ # Hardcode the URL resolving to the IP we want.
684
+ self.lookups["example.com"] = [
685
+ (IPv4Address, "1.1.1.2"),
686
+ (IPv4Address, "10.1.2.3"),
687
+ ]
688
+
689
+ channel = self.make_request(
690
+ "GET",
691
+ "/_matrix/client/v1/media/preview_url?url=http://example.com",
692
+ shorthand=False,
693
+ )
694
+ self.assertEqual(channel.code, 502)
695
+ self.assertEqual(
696
+ channel.json_body,
697
+ {
698
+ "errcode": "M_UNKNOWN",
699
+ "error": "DNS resolution failure during URL preview generation",
700
+ },
701
+ )
702
+
703
+ def test_blocked_ipv6_specific(self) -> None:
704
+ """
705
+ Blocked IP addresses, found via DNS, are not spidered.
706
+ """
707
+ self.lookups["example.com"] = [
708
+ (IPv6Address, "3fff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")
709
+ ]
710
+
711
+ channel = self.make_request(
712
+ "GET",
713
+ "/_matrix/client/v1/media/preview_url?url=http://example.com",
714
+ shorthand=False,
715
+ )
716
+
717
+ # No requests made.
718
+ self.assertEqual(len(self.reactor.tcpClients), 0)
719
+ self.assertEqual(channel.code, 502)
720
+ self.assertEqual(
721
+ channel.json_body,
722
+ {
723
+ "errcode": "M_UNKNOWN",
724
+ "error": "DNS resolution failure during URL preview generation",
725
+ },
726
+ )
727
+
728
+ def test_blocked_ipv6_range(self) -> None:
729
+ """
730
+ Blocked IP ranges, IPs found over DNS, are not spidered.
731
+ """
732
+ self.lookups["example.com"] = [(IPv6Address, "2001:800::1")]
733
+
734
+ channel = self.make_request(
735
+ "GET",
736
+ "/_matrix/client/v1/media/preview_url?url=http://example.com",
737
+ shorthand=False,
738
+ )
739
+
740
+ self.assertEqual(channel.code, 502)
741
+ self.assertEqual(
742
+ channel.json_body,
743
+ {
744
+ "errcode": "M_UNKNOWN",
745
+ "error": "DNS resolution failure during URL preview generation",
746
+ },
747
+ )
748
+
749
+ def test_OPTIONS(self) -> None:
750
+ """
751
+ OPTIONS returns the OPTIONS.
752
+ """
753
+ channel = self.make_request(
754
+ "OPTIONS",
755
+ "/_matrix/client/v1/media/preview_url?url=http://example.com",
756
+ shorthand=False,
757
+ )
758
+ self.assertEqual(channel.code, 204)
759
+
760
+ def test_accept_language_config_option(self) -> None:
761
+ """
762
+ Accept-Language header is sent to the remote server
763
+ """
764
+ self.lookups["example.com"] = [(IPv4Address, "10.1.2.3")]
765
+
766
+ # Build and make a request to the server
767
+ channel = self.make_request(
768
+ "GET",
769
+ "/_matrix/client/v1/media/preview_url?url=http://example.com",
770
+ shorthand=False,
771
+ await_result=False,
772
+ )
773
+ self.pump()
774
+
775
+ # Extract Synapse's tcp client
776
+ client = self.reactor.tcpClients[0][2].buildProtocol(None)
777
+
778
+ # Build a fake remote server to reply with
779
+ server = AccumulatingProtocol()
780
+
781
+ # Connect the two together
782
+ server.makeConnection(FakeTransport(client, self.reactor))
783
+ client.makeConnection(FakeTransport(server, self.reactor))
784
+
785
+ # Tell Synapse that it has received some data from the remote server
786
+ client.dataReceived(
787
+ b"HTTP/1.0 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n"
788
+ % (len(self.end_content),)
789
+ + self.end_content
790
+ )
791
+
792
+ # Move the reactor along until we get a response on our original channel
793
+ self.pump()
794
+ self.assertEqual(channel.code, 200)
795
+ self.assertEqual(
796
+ channel.json_body, {"og:title": "~matrix~", "og:description": "hi"}
797
+ )
798
+
799
+ # Check that the server received the Accept-Language header as part
800
+ # of the request from Synapse
801
+ self.assertIn(
802
+ (
803
+ b"Accept-Language: en-UK\r\n"
804
+ b"Accept-Language: en-US;q=0.9\r\n"
805
+ b"Accept-Language: fr;q=0.8\r\n"
806
+ b"Accept-Language: *;q=0.7"
807
+ ),
808
+ server.data,
809
+ )
810
+
811
+ def test_image(self) -> None:
812
+ """An image should be precached if mentioned in the HTML."""
813
+ self.lookups["matrix.org"] = [(IPv4Address, "10.1.2.3")]
814
+ self.lookups["cdn.matrix.org"] = [(IPv4Address, "10.1.2.4")]
815
+
816
+ result = (
817
+ b"""<html><body><img src="http://cdn.matrix.org/foo.png"></body></html>"""
818
+ )
819
+
820
+ channel = self.make_request(
821
+ "GET",
822
+ "/_matrix/client/v1/media/preview_url?url=http://matrix.org",
823
+ shorthand=False,
824
+ await_result=False,
825
+ )
826
+ self.pump()
827
+
828
+ # Respond with the HTML.
829
+ client = self.reactor.tcpClients[0][2].buildProtocol(None)
830
+ server = AccumulatingProtocol()
831
+ server.makeConnection(FakeTransport(client, self.reactor))
832
+ client.makeConnection(FakeTransport(server, self.reactor))
833
+ client.dataReceived(
834
+ (
835
+ b"HTTP/1.0 200 OK\r\nContent-Length: %d\r\n"
836
+ b'Content-Type: text/html; charset="utf8"\r\n\r\n'
837
+ )
838
+ % (len(result),)
839
+ + result
840
+ )
841
+ self.pump()
842
+
843
+ # Respond with the photo.
844
+ client = self.reactor.tcpClients[1][2].buildProtocol(None)
845
+ server = AccumulatingProtocol()
846
+ server.makeConnection(FakeTransport(client, self.reactor))
847
+ client.makeConnection(FakeTransport(server, self.reactor))
848
+ client.dataReceived(
849
+ (
850
+ b"HTTP/1.0 200 OK\r\nContent-Length: %d\r\n"
851
+ b"Content-Type: image/png\r\n\r\n"
852
+ )
853
+ % (len(SMALL_PNG),)
854
+ + SMALL_PNG
855
+ )
856
+ self.pump()
857
+
858
+ # The image should be in the result.
859
+ self.assertEqual(channel.code, 200)
860
+ self._assert_small_png(channel.json_body)
861
+
862
+ def test_nonexistent_image(self) -> None:
863
+ """If the preview image doesn't exist, ensure some data is returned."""
864
+ self.lookups["matrix.org"] = [(IPv4Address, "10.1.2.3")]
865
+
866
+ result = (
867
+ b"""<html><body><img src="http://cdn.matrix.org/foo.jpg"></body></html>"""
868
+ )
869
+
870
+ channel = self.make_request(
871
+ "GET",
872
+ "/_matrix/client/v1/media/preview_url?url=http://matrix.org",
873
+ shorthand=False,
874
+ await_result=False,
875
+ )
876
+ self.pump()
877
+
878
+ client = self.reactor.tcpClients[0][2].buildProtocol(None)
879
+ server = AccumulatingProtocol()
880
+ server.makeConnection(FakeTransport(client, self.reactor))
881
+ client.makeConnection(FakeTransport(server, self.reactor))
882
+ client.dataReceived(
883
+ (
884
+ b"HTTP/1.0 200 OK\r\nContent-Length: %d\r\n"
885
+ b'Content-Type: text/html; charset="utf8"\r\n\r\n'
886
+ )
887
+ % (len(result),)
888
+ + result
889
+ )
890
+
891
+ self.pump()
892
+
893
+ # There should not be a second connection.
894
+ self.assertEqual(len(self.reactor.tcpClients), 1)
895
+
896
+ # The image should not be in the result.
897
+ self.assertEqual(channel.code, 200)
898
+ self.assertNotIn("og:image", channel.json_body)
899
+
900
+ @unittest.override_config(
901
+ {"url_preview_url_blacklist": [{"netloc": "cdn.matrix.org"}]}
902
+ )
903
+ def test_image_blocked(self) -> None:
904
+ """If the preview image doesn't exist, ensure some data is returned."""
905
+ self.lookups["matrix.org"] = [(IPv4Address, "10.1.2.3")]
906
+ self.lookups["cdn.matrix.org"] = [(IPv4Address, "10.1.2.4")]
907
+
908
+ result = (
909
+ b"""<html><body><img src="http://cdn.matrix.org/foo.jpg"></body></html>"""
910
+ )
911
+
912
+ channel = self.make_request(
913
+ "GET",
914
+ "/_matrix/client/v1/media/preview_url?url=http://matrix.org",
915
+ shorthand=False,
916
+ await_result=False,
917
+ )
918
+ self.pump()
919
+
920
+ client = self.reactor.tcpClients[0][2].buildProtocol(None)
921
+ server = AccumulatingProtocol()
922
+ server.makeConnection(FakeTransport(client, self.reactor))
923
+ client.makeConnection(FakeTransport(server, self.reactor))
924
+ client.dataReceived(
925
+ (
926
+ b"HTTP/1.0 200 OK\r\nContent-Length: %d\r\n"
927
+ b'Content-Type: text/html; charset="utf8"\r\n\r\n'
928
+ )
929
+ % (len(result),)
930
+ + result
931
+ )
932
+ self.pump()
933
+
934
+ # There should not be a second connection.
935
+ self.assertEqual(len(self.reactor.tcpClients), 1)
936
+
937
+ # The image should not be in the result.
938
+ self.assertEqual(channel.code, 200)
939
+ self.assertNotIn("og:image", channel.json_body)
940
+
941
+ def test_oembed_failure(self) -> None:
942
+ """If the autodiscovered oEmbed URL fails, ensure some data is returned."""
943
+ self.lookups["matrix.org"] = [(IPv4Address, "10.1.2.3")]
944
+
945
+ result = b"""
946
+ <title>oEmbed Autodiscovery Fail</title>
947
+ <link rel="alternate" type="application/json+oembed"
948
+ href="http://example.com/oembed?url=http%3A%2F%2Fmatrix.org&format=json"
949
+ title="matrixdotorg" />
950
+ """
951
+
952
+ channel = self.make_request(
953
+ "GET",
954
+ "/_matrix/client/v1/media/preview_url?url=http://matrix.org",
955
+ shorthand=False,
956
+ await_result=False,
957
+ )
958
+ self.pump()
959
+
960
+ client = self.reactor.tcpClients[0][2].buildProtocol(None)
961
+ server = AccumulatingProtocol()
962
+ server.makeConnection(FakeTransport(client, self.reactor))
963
+ client.makeConnection(FakeTransport(server, self.reactor))
964
+ client.dataReceived(
965
+ (
966
+ b"HTTP/1.0 200 OK\r\nContent-Length: %d\r\n"
967
+ b'Content-Type: text/html; charset="utf8"\r\n\r\n'
968
+ )
969
+ % (len(result),)
970
+ + result
971
+ )
972
+
973
+ self.pump()
974
+ self.assertEqual(channel.code, 200)
975
+
976
+ # The image should not be in the result.
977
+ self.assertEqual(channel.json_body["og:title"], "oEmbed Autodiscovery Fail")
978
+
979
+ def test_data_url(self) -> None:
980
+ """
981
+ Requesting to preview a data URL is not supported.
982
+ """
983
+ self.lookups["matrix.org"] = [(IPv4Address, "10.1.2.3")]
984
+
985
+ data = base64.b64encode(SMALL_PNG).decode()
986
+
987
+ query_params = urlencode(
988
+ {
989
+ "url": f'<html><head><img src="data:image/png;base64,{data}" /></head></html>'
990
+ }
991
+ )
992
+
993
+ channel = self.make_request(
994
+ "GET",
995
+ f"/_matrix/client/v1/media/preview_url?{query_params}",
996
+ shorthand=False,
997
+ )
998
+ self.pump()
999
+
1000
+ self.assertEqual(channel.code, 500)
1001
+
1002
+ def test_inline_data_url(self) -> None:
1003
+ """
1004
+ An inline image (as a data URL) should be parsed properly.
1005
+ """
1006
+ self.lookups["matrix.org"] = [(IPv4Address, "10.1.2.3")]
1007
+
1008
+ data = base64.b64encode(SMALL_PNG)
1009
+
1010
+ end_content = (
1011
+ b'<html><head><img src="data:image/png;base64,%s" /></head></html>'
1012
+ ) % (data,)
1013
+
1014
+ channel = self.make_request(
1015
+ "GET",
1016
+ "/_matrix/client/v1/media/preview_url?url=http://matrix.org",
1017
+ shorthand=False,
1018
+ await_result=False,
1019
+ )
1020
+ self.pump()
1021
+
1022
+ client = self.reactor.tcpClients[0][2].buildProtocol(None)
1023
+ server = AccumulatingProtocol()
1024
+ server.makeConnection(FakeTransport(client, self.reactor))
1025
+ client.makeConnection(FakeTransport(server, self.reactor))
1026
+ client.dataReceived(
1027
+ (
1028
+ b"HTTP/1.0 200 OK\r\nContent-Length: %d\r\n"
1029
+ b'Content-Type: text/html; charset="utf8"\r\n\r\n'
1030
+ )
1031
+ % (len(end_content),)
1032
+ + end_content
1033
+ )
1034
+
1035
+ self.pump()
1036
+ self.assertEqual(channel.code, 200)
1037
+ self._assert_small_png(channel.json_body)
1038
+
1039
+ def test_oembed_photo(self) -> None:
1040
+ """Test an oEmbed endpoint which returns a 'photo' type which redirects the preview to a new URL."""
1041
+ self.lookups["publish.twitter.com"] = [(IPv4Address, "10.1.2.3")]
1042
+ self.lookups["cdn.twitter.com"] = [(IPv4Address, "10.1.2.3")]
1043
+
1044
+ result = {
1045
+ "version": "1.0",
1046
+ "type": "photo",
1047
+ "url": "http://cdn.twitter.com/matrixdotorg",
1048
+ }
1049
+ oembed_content = json.dumps(result).encode("utf-8")
1050
+
1051
+ channel = self.make_request(
1052
+ "GET",
1053
+ "/_matrix/client/v1/media/preview_url?url=http://twitter.com/matrixdotorg/status/12345",
1054
+ shorthand=False,
1055
+ await_result=False,
1056
+ )
1057
+ self.pump()
1058
+
1059
+ client = self.reactor.tcpClients[0][2].buildProtocol(None)
1060
+ server = AccumulatingProtocol()
1061
+ server.makeConnection(FakeTransport(client, self.reactor))
1062
+ client.makeConnection(FakeTransport(server, self.reactor))
1063
+ client.dataReceived(
1064
+ (
1065
+ b"HTTP/1.0 200 OK\r\nContent-Length: %d\r\n"
1066
+ b'Content-Type: application/json; charset="utf8"\r\n\r\n'
1067
+ )
1068
+ % (len(oembed_content),)
1069
+ + oembed_content
1070
+ )
1071
+
1072
+ self.pump()
1073
+
1074
+ # Ensure a second request is made to the photo URL.
1075
+ client = self.reactor.tcpClients[1][2].buildProtocol(None)
1076
+ server = AccumulatingProtocol()
1077
+ server.makeConnection(FakeTransport(client, self.reactor))
1078
+ client.makeConnection(FakeTransport(server, self.reactor))
1079
+ client.dataReceived(
1080
+ (
1081
+ b"HTTP/1.0 200 OK\r\nContent-Length: %d\r\n"
1082
+ b"Content-Type: image/png\r\n\r\n"
1083
+ )
1084
+ % (len(SMALL_PNG),)
1085
+ + SMALL_PNG
1086
+ )
1087
+
1088
+ self.pump()
1089
+
1090
+ # Ensure the URL is what was requested.
1091
+ self.assertIn(b"/matrixdotorg", server.data)
1092
+
1093
+ self.assertEqual(channel.code, 200)
1094
+ body = channel.json_body
1095
+ self.assertEqual(body["og:url"], "http://twitter.com/matrixdotorg/status/12345")
1096
+ self._assert_small_png(body)
1097
+
1098
+ def test_oembed_rich(self) -> None:
1099
+ """Test an oEmbed endpoint which returns HTML content via the 'rich' type."""
1100
+ self.lookups["publish.twitter.com"] = [(IPv4Address, "10.1.2.3")]
1101
+
1102
+ result = {
1103
+ "version": "1.0",
1104
+ "type": "rich",
1105
+ # Note that this provides the author, not the title.
1106
+ "author_name": "Alice",
1107
+ "html": "<div>Content Preview</div>",
1108
+ }
1109
+ end_content = json.dumps(result).encode("utf-8")
1110
+
1111
+ channel = self.make_request(
1112
+ "GET",
1113
+ "/_matrix/client/v1/media/preview_url?url=http://twitter.com/matrixdotorg/status/12345",
1114
+ shorthand=False,
1115
+ await_result=False,
1116
+ )
1117
+ self.pump()
1118
+
1119
+ client = self.reactor.tcpClients[0][2].buildProtocol(None)
1120
+ server = AccumulatingProtocol()
1121
+ server.makeConnection(FakeTransport(client, self.reactor))
1122
+ client.makeConnection(FakeTransport(server, self.reactor))
1123
+ client.dataReceived(
1124
+ (
1125
+ b"HTTP/1.0 200 OK\r\nContent-Length: %d\r\n"
1126
+ b'Content-Type: application/json; charset="utf8"\r\n\r\n'
1127
+ )
1128
+ % (len(end_content),)
1129
+ + end_content
1130
+ )
1131
+
1132
+ self.pump()
1133
+
1134
+ # Double check that the proper host is being connected to. (Note that
1135
+ # twitter.com can't be resolved so this is already implicitly checked.)
1136
+ self.assertIn(b"\r\nHost: publish.twitter.com\r\n", server.data)
1137
+
1138
+ self.assertEqual(channel.code, 200)
1139
+ body = channel.json_body
1140
+ self.assertEqual(
1141
+ body,
1142
+ {
1143
+ "og:url": "http://twitter.com/matrixdotorg/status/12345",
1144
+ "og:title": "Alice",
1145
+ "og:description": "Content Preview",
1146
+ },
1147
+ )
1148
+
1149
+ def test_oembed_format(self) -> None:
1150
+ """Test an oEmbed endpoint which requires the format in the URL."""
1151
+ self.lookups["www.hulu.com"] = [(IPv4Address, "10.1.2.3")]
1152
+
1153
+ result = {
1154
+ "version": "1.0",
1155
+ "type": "rich",
1156
+ "html": "<div>Content Preview</div>",
1157
+ }
1158
+ end_content = json.dumps(result).encode("utf-8")
1159
+
1160
+ channel = self.make_request(
1161
+ "GET",
1162
+ "/_matrix/client/v1/media/preview_url?url=http://www.hulu.com/watch/12345",
1163
+ shorthand=False,
1164
+ await_result=False,
1165
+ )
1166
+ self.pump()
1167
+
1168
+ client = self.reactor.tcpClients[0][2].buildProtocol(None)
1169
+ server = AccumulatingProtocol()
1170
+ server.makeConnection(FakeTransport(client, self.reactor))
1171
+ client.makeConnection(FakeTransport(server, self.reactor))
1172
+ client.dataReceived(
1173
+ (
1174
+ b"HTTP/1.0 200 OK\r\nContent-Length: %d\r\n"
1175
+ b'Content-Type: application/json; charset="utf8"\r\n\r\n'
1176
+ )
1177
+ % (len(end_content),)
1178
+ + end_content
1179
+ )
1180
+
1181
+ self.pump()
1182
+
1183
+ # The {format} should have been turned into json.
1184
+ self.assertIn(b"/api/oembed.json", server.data)
1185
+ # A URL parameter of format=json should be provided.
1186
+ self.assertIn(b"format=json", server.data)
1187
+
1188
+ self.assertEqual(channel.code, 200)
1189
+ body = channel.json_body
1190
+ self.assertEqual(
1191
+ body,
1192
+ {
1193
+ "og:url": "http://www.hulu.com/watch/12345",
1194
+ "og:description": "Content Preview",
1195
+ },
1196
+ )
1197
+
1198
+ @unittest.override_config(
1199
+ {"url_preview_url_blacklist": [{"netloc": "publish.twitter.com"}]}
1200
+ )
1201
+ def test_oembed_blocked(self) -> None:
1202
+ """The oEmbed URL should not be downloaded if the oEmbed URL is blocked."""
1203
+ self.lookups["twitter.com"] = [(IPv4Address, "10.1.2.3")]
1204
+
1205
+ channel = self.make_request(
1206
+ "GET",
1207
+ "/_matrix/client/v1/media/preview_url?url=http://twitter.com/matrixdotorg/status/12345",
1208
+ shorthand=False,
1209
+ await_result=False,
1210
+ )
1211
+ self.pump()
1212
+ self.assertEqual(channel.code, 403, channel.result)
1213
+
1214
+ def test_oembed_autodiscovery(self) -> None:
1215
+ """
1216
+ Autodiscovery works by finding the link in the HTML response and then requesting an oEmbed URL.
1217
+ 1. Request a preview of a URL which is not known to the oEmbed code.
1218
+ 2. It returns HTML including a link to an oEmbed preview.
1219
+ 3. The oEmbed preview is requested and returns a URL for an image.
1220
+ 4. The image is requested for thumbnailing.
1221
+ """
1222
+ # This is a little cheesy in that we use the www subdomain (which isn't the
1223
+ # list of oEmbed patterns) to get "raw" HTML response.
1224
+ self.lookups["www.twitter.com"] = [(IPv4Address, "10.1.2.3")]
1225
+ self.lookups["publish.twitter.com"] = [(IPv4Address, "10.1.2.3")]
1226
+ self.lookups["cdn.twitter.com"] = [(IPv4Address, "10.1.2.3")]
1227
+
1228
+ result = b"""
1229
+ <link rel="alternate" type="application/json+oembed"
1230
+ href="http://publish.twitter.com/oembed?url=http%3A%2F%2Fcdn.twitter.com%2Fmatrixdotorg%2Fstatus%2F12345&format=json"
1231
+ title="matrixdotorg" />
1232
+ """
1233
+
1234
+ channel = self.make_request(
1235
+ "GET",
1236
+ "/_matrix/client/v1/media/preview_url?url=http://www.twitter.com/matrixdotorg/status/12345",
1237
+ shorthand=False,
1238
+ await_result=False,
1239
+ )
1240
+ self.pump()
1241
+
1242
+ client = self.reactor.tcpClients[0][2].buildProtocol(None)
1243
+ server = AccumulatingProtocol()
1244
+ server.makeConnection(FakeTransport(client, self.reactor))
1245
+ client.makeConnection(FakeTransport(server, self.reactor))
1246
+ client.dataReceived(
1247
+ (
1248
+ b"HTTP/1.0 200 OK\r\nContent-Length: %d\r\n"
1249
+ b'Content-Type: text/html; charset="utf8"\r\n\r\n'
1250
+ )
1251
+ % (len(result),)
1252
+ + result
1253
+ )
1254
+ self.pump()
1255
+
1256
+ # The oEmbed response.
1257
+ result2 = {
1258
+ "version": "1.0",
1259
+ "type": "photo",
1260
+ "url": "http://cdn.twitter.com/matrixdotorg",
1261
+ }
1262
+ oembed_content = json.dumps(result2).encode("utf-8")
1263
+
1264
+ # Ensure a second request is made to the oEmbed URL.
1265
+ client = self.reactor.tcpClients[1][2].buildProtocol(None)
1266
+ server = AccumulatingProtocol()
1267
+ server.makeConnection(FakeTransport(client, self.reactor))
1268
+ client.makeConnection(FakeTransport(server, self.reactor))
1269
+ client.dataReceived(
1270
+ (
1271
+ b"HTTP/1.0 200 OK\r\nContent-Length: %d\r\n"
1272
+ b'Content-Type: application/json; charset="utf8"\r\n\r\n'
1273
+ )
1274
+ % (len(oembed_content),)
1275
+ + oembed_content
1276
+ )
1277
+ self.pump()
1278
+
1279
+ # Ensure the URL is what was requested.
1280
+ self.assertIn(b"/oembed?", server.data)
1281
+
1282
+ # Ensure a third request is made to the photo URL.
1283
+ client = self.reactor.tcpClients[2][2].buildProtocol(None)
1284
+ server = AccumulatingProtocol()
1285
+ server.makeConnection(FakeTransport(client, self.reactor))
1286
+ client.makeConnection(FakeTransport(server, self.reactor))
1287
+ client.dataReceived(
1288
+ (
1289
+ b"HTTP/1.0 200 OK\r\nContent-Length: %d\r\n"
1290
+ b"Content-Type: image/png\r\n\r\n"
1291
+ )
1292
+ % (len(SMALL_PNG),)
1293
+ + SMALL_PNG
1294
+ )
1295
+ self.pump()
1296
+
1297
+ # Ensure the URL is what was requested.
1298
+ self.assertIn(b"/matrixdotorg", server.data)
1299
+
1300
+ self.assertEqual(channel.code, 200)
1301
+ body = channel.json_body
1302
+ self.assertEqual(
1303
+ body["og:url"], "http://www.twitter.com/matrixdotorg/status/12345"
1304
+ )
1305
+ self._assert_small_png(body)
1306
+
1307
+ @unittest.override_config(
1308
+ {"url_preview_url_blacklist": [{"netloc": "publish.twitter.com"}]}
1309
+ )
1310
+ def test_oembed_autodiscovery_blocked(self) -> None:
1311
+ """
1312
+ If the discovered oEmbed URL is blocked, it should be discarded.
1313
+ """
1314
+ # This is a little cheesy in that we use the www subdomain (which isn't the
1315
+ # list of oEmbed patterns) to get "raw" HTML response.
1316
+ self.lookups["www.twitter.com"] = [(IPv4Address, "10.1.2.3")]
1317
+ self.lookups["publish.twitter.com"] = [(IPv4Address, "10.1.2.4")]
1318
+
1319
+ result = b"""
1320
+ <title>Test</title>
1321
+ <link rel="alternate" type="application/json+oembed"
1322
+ href="http://publish.twitter.com/oembed?url=http%3A%2F%2Fcdn.twitter.com%2Fmatrixdotorg%2Fstatus%2F12345&format=json"
1323
+ title="matrixdotorg" />
1324
+ """
1325
+
1326
+ channel = self.make_request(
1327
+ "GET",
1328
+ "/_matrix/client/v1/media/preview_url?url=http://www.twitter.com/matrixdotorg/status/12345",
1329
+ shorthand=False,
1330
+ await_result=False,
1331
+ )
1332
+ self.pump()
1333
+
1334
+ client = self.reactor.tcpClients[0][2].buildProtocol(None)
1335
+ server = AccumulatingProtocol()
1336
+ server.makeConnection(FakeTransport(client, self.reactor))
1337
+ client.makeConnection(FakeTransport(server, self.reactor))
1338
+ client.dataReceived(
1339
+ (
1340
+ b"HTTP/1.0 200 OK\r\nContent-Length: %d\r\n"
1341
+ b'Content-Type: text/html; charset="utf8"\r\n\r\n'
1342
+ )
1343
+ % (len(result),)
1344
+ + result
1345
+ )
1346
+
1347
+ self.pump()
1348
+
1349
+ # Ensure there's no additional connections.
1350
+ self.assertEqual(len(self.reactor.tcpClients), 1)
1351
+
1352
+ # Ensure the URL is what was requested.
1353
+ self.assertIn(b"\r\nHost: www.twitter.com\r\n", server.data)
1354
+
1355
+ self.assertEqual(channel.code, 200)
1356
+ body = channel.json_body
1357
+ self.assertEqual(body["og:title"], "Test")
1358
+ self.assertNotIn("og:image", body)
1359
+
1360
+ def _download_image(self) -> tuple[str, str]:
1361
+ """Downloads an image into the URL cache.
1362
+ Returns:
1363
+ A (host, media_id) tuple representing the MXC URI of the image.
1364
+ """
1365
+ self.lookups["cdn.twitter.com"] = [(IPv4Address, "10.1.2.3")]
1366
+
1367
+ channel = self.make_request(
1368
+ "GET",
1369
+ "/_matrix/client/v1/media/preview_url?url=http://cdn.twitter.com/matrixdotorg",
1370
+ shorthand=False,
1371
+ await_result=False,
1372
+ )
1373
+ self.pump()
1374
+
1375
+ client = self.reactor.tcpClients[0][2].buildProtocol(None)
1376
+ server = AccumulatingProtocol()
1377
+ server.makeConnection(FakeTransport(client, self.reactor))
1378
+ client.makeConnection(FakeTransport(server, self.reactor))
1379
+ client.dataReceived(
1380
+ b"HTTP/1.0 200 OK\r\nContent-Length: %d\r\nContent-Type: image/png\r\n\r\n"
1381
+ % (len(SMALL_PNG),)
1382
+ + SMALL_PNG
1383
+ )
1384
+
1385
+ self.pump()
1386
+ self.assertEqual(channel.code, 200)
1387
+ body = channel.json_body
1388
+ mxc_uri = body["og:image"]
1389
+ host, _port, media_id = parse_and_validate_mxc_uri(mxc_uri)
1390
+ self.assertIsNone(_port)
1391
+ return host, media_id
1392
+
1393
+ def test_storage_providers_exclude_files(self) -> None:
1394
+ """Test that files are not stored in or fetched from storage providers."""
1395
+ host, media_id = self._download_image()
1396
+
1397
+ rel_file_path = self.media_repo.filepaths.url_cache_filepath_rel(media_id)
1398
+ media_store_path = os.path.join(self.media_store_path, rel_file_path)
1399
+ storage_provider_path = os.path.join(self.storage_path, rel_file_path)
1400
+
1401
+ # Check storage
1402
+ self.assertTrue(os.path.isfile(media_store_path))
1403
+ self.assertFalse(
1404
+ os.path.isfile(storage_provider_path),
1405
+ "URL cache file was unexpectedly stored in a storage provider",
1406
+ )
1407
+
1408
+ # Check fetching
1409
+ channel = self.make_request(
1410
+ "GET",
1411
+ f"/_matrix/client/v1/media/download/{host}/{media_id}",
1412
+ shorthand=False,
1413
+ await_result=False,
1414
+ )
1415
+ self.pump()
1416
+ self.assertEqual(channel.code, 200)
1417
+
1418
+ # Move cached file into the storage provider
1419
+ os.makedirs(os.path.dirname(storage_provider_path), exist_ok=True)
1420
+ os.rename(media_store_path, storage_provider_path)
1421
+
1422
+ channel = self.make_request(
1423
+ "GET",
1424
+ f"/_matrix/client/v1/download/{host}/{media_id}",
1425
+ shorthand=False,
1426
+ await_result=False,
1427
+ )
1428
+ self.pump()
1429
+ self.assertEqual(
1430
+ channel.code,
1431
+ 404,
1432
+ "URL cache file was unexpectedly retrieved from a storage provider",
1433
+ )
1434
+
1435
+ def test_storage_providers_exclude_thumbnails(self) -> None:
1436
+ """Test that thumbnails are not stored in or fetched from storage providers."""
1437
+ host, media_id = self._download_image()
1438
+
1439
+ rel_thumbnail_path = (
1440
+ self.media_repo.filepaths.url_cache_thumbnail_directory_rel(media_id)
1441
+ )
1442
+ media_store_thumbnail_path = os.path.join(
1443
+ self.media_store_path, rel_thumbnail_path
1444
+ )
1445
+ storage_provider_thumbnail_path = os.path.join(
1446
+ self.storage_path, rel_thumbnail_path
1447
+ )
1448
+
1449
+ # Check storage
1450
+ self.assertTrue(os.path.isdir(media_store_thumbnail_path))
1451
+ self.assertFalse(
1452
+ os.path.isdir(storage_provider_thumbnail_path),
1453
+ "URL cache thumbnails were unexpectedly stored in a storage provider",
1454
+ )
1455
+
1456
+ # Check fetching
1457
+ channel = self.make_request(
1458
+ "GET",
1459
+ f"/_matrix/client/v1/media/thumbnail/{host}/{media_id}?width=32&height=32&method=scale",
1460
+ shorthand=False,
1461
+ await_result=False,
1462
+ )
1463
+ self.pump()
1464
+ self.assertEqual(channel.code, 200)
1465
+
1466
+ # Remove the original, otherwise thumbnails will regenerate
1467
+ rel_file_path = self.media_repo.filepaths.url_cache_filepath_rel(media_id)
1468
+ media_store_path = os.path.join(self.media_store_path, rel_file_path)
1469
+ os.remove(media_store_path)
1470
+
1471
+ # Move cached thumbnails into the storage provider
1472
+ os.makedirs(os.path.dirname(storage_provider_thumbnail_path), exist_ok=True)
1473
+ os.rename(media_store_thumbnail_path, storage_provider_thumbnail_path)
1474
+
1475
+ channel = self.make_request(
1476
+ "GET",
1477
+ f"/_matrix/client/v1/media/thumbnail/{host}/{media_id}?width=32&height=32&method=scale",
1478
+ shorthand=False,
1479
+ await_result=False,
1480
+ )
1481
+ self.pump()
1482
+ self.assertEqual(
1483
+ channel.code,
1484
+ 404,
1485
+ "URL cache thumbnail was unexpectedly retrieved from a storage provider",
1486
+ )
1487
+
1488
+ def test_cache_expiry(self) -> None:
1489
+ """Test that URL cache files and thumbnails are cleaned up properly on expiry."""
1490
+ _host, media_id = self._download_image()
1491
+
1492
+ file_path = self.media_repo.filepaths.url_cache_filepath(media_id)
1493
+ file_dirs = self.media_repo.filepaths.url_cache_filepath_dirs_to_delete(
1494
+ media_id
1495
+ )
1496
+ thumbnail_dir = self.media_repo.filepaths.url_cache_thumbnail_directory(
1497
+ media_id
1498
+ )
1499
+ thumbnail_dirs = self.media_repo.filepaths.url_cache_thumbnail_dirs_to_delete(
1500
+ media_id
1501
+ )
1502
+
1503
+ self.assertTrue(os.path.isfile(file_path))
1504
+ self.assertTrue(os.path.isdir(thumbnail_dir))
1505
+
1506
+ self.reactor.advance(IMAGE_CACHE_EXPIRY_MS * 1000 + 1)
1507
+ self.get_success(self.url_previewer._expire_url_cache_data())
1508
+
1509
+ for path in [file_path] + file_dirs + [thumbnail_dir] + thumbnail_dirs:
1510
+ self.assertFalse(
1511
+ os.path.exists(path),
1512
+ f"{os.path.relpath(path, self.media_store_path)} was not deleted",
1513
+ )
1514
+
1515
+ @unittest.override_config({"url_preview_url_blacklist": [{"port": "*"}]})
1516
+ def test_blocked_port(self) -> None:
1517
+ """Tests that blocking URLs with a port makes previewing such URLs
1518
+ fail with a 403 error and doesn't impact other previews.
1519
+ """
1520
+ self.lookups["matrix.org"] = [(IPv4Address, "10.1.2.3")]
1521
+
1522
+ bad_url = quote("http://matrix.org:8888/foo")
1523
+ good_url = quote("http://matrix.org/foo")
1524
+
1525
+ channel = self.make_request(
1526
+ "GET",
1527
+ "/_matrix/client/v1/media/preview_url?url=" + bad_url,
1528
+ shorthand=False,
1529
+ await_result=False,
1530
+ )
1531
+ self.pump()
1532
+ self.assertEqual(channel.code, 403, channel.result)
1533
+
1534
+ channel = self.make_request(
1535
+ "GET",
1536
+ "/_matrix/client/v1/media/preview_url?url=" + good_url,
1537
+ shorthand=False,
1538
+ await_result=False,
1539
+ )
1540
+ self.pump()
1541
+
1542
+ client = self.reactor.tcpClients[0][2].buildProtocol(None)
1543
+ server = AccumulatingProtocol()
1544
+ server.makeConnection(FakeTransport(client, self.reactor))
1545
+ client.makeConnection(FakeTransport(server, self.reactor))
1546
+ client.dataReceived(
1547
+ b"HTTP/1.0 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n"
1548
+ % (len(self.end_content),)
1549
+ + self.end_content
1550
+ )
1551
+
1552
+ self.pump()
1553
+ self.assertEqual(channel.code, 200)
1554
+
1555
+ @unittest.override_config(
1556
+ {"url_preview_url_blacklist": [{"netloc": "example.com"}]}
1557
+ )
1558
+ def test_blocked_url(self) -> None:
1559
+ """Tests that blocking URLs with a host makes previewing such URLs
1560
+ fail with a 403 error.
1561
+ """
1562
+ self.lookups["example.com"] = [(IPv4Address, "10.1.2.3")]
1563
+
1564
+ bad_url = quote("http://example.com/foo")
1565
+
1566
+ channel = self.make_request(
1567
+ "GET",
1568
+ "/_matrix/client/v1/media/preview_url?url=" + bad_url,
1569
+ shorthand=False,
1570
+ await_result=False,
1571
+ )
1572
+ self.pump()
1573
+ self.assertEqual(channel.code, 403, channel.result)
1574
+
1575
+
1576
+ class MediaConfigTest(unittest.HomeserverTestCase):
1577
+ servlets = [
1578
+ media.register_servlets,
1579
+ admin.register_servlets,
1580
+ login.register_servlets,
1581
+ ]
1582
+
1583
+ def make_homeserver(
1584
+ self, reactor: ThreadedMemoryReactorClock, clock: Clock
1585
+ ) -> HomeServer:
1586
+ config = self.default_config()
1587
+
1588
+ self.storage_path = self.mktemp()
1589
+ self.media_store_path = self.mktemp()
1590
+ os.mkdir(self.storage_path)
1591
+ os.mkdir(self.media_store_path)
1592
+ config["media_store_path"] = self.media_store_path
1593
+
1594
+ provider_config = {
1595
+ "module": "synapse.media.storage_provider.FileStorageProviderBackend",
1596
+ "store_local": True,
1597
+ "store_synchronous": False,
1598
+ "store_remote": True,
1599
+ "config": {"directory": self.storage_path},
1600
+ }
1601
+
1602
+ config["media_storage_providers"] = [provider_config]
1603
+
1604
+ return self.setup_test_homeserver(config=config)
1605
+
1606
+ def prepare(self, reactor: MemoryReactor, clock: Clock, hs: HomeServer) -> None:
1607
+ self.register_user("user", "password")
1608
+ self.tok = self.login("user", "password")
1609
+
1610
+ def test_media_config(self) -> None:
1611
+ channel = self.make_request(
1612
+ "GET",
1613
+ "/_matrix/client/v1/media/config",
1614
+ shorthand=False,
1615
+ access_token=self.tok,
1616
+ )
1617
+ self.assertEqual(channel.code, 200)
1618
+ self.assertEqual(
1619
+ channel.json_body["m.upload.size"], self.hs.config.media.max_upload_size
1620
+ )
1621
+
1622
+
1623
+ class MediaConfigModuleCallbackTestCase(unittest.HomeserverTestCase):
1624
+ servlets = [
1625
+ media.register_servlets,
1626
+ admin.register_servlets,
1627
+ login.register_servlets,
1628
+ ]
1629
+
1630
+ def make_homeserver(
1631
+ self, reactor: ThreadedMemoryReactorClock, clock: Clock
1632
+ ) -> HomeServer:
1633
+ config = self.default_config()
1634
+
1635
+ self.storage_path = self.mktemp()
1636
+ self.media_store_path = self.mktemp()
1637
+ os.mkdir(self.storage_path)
1638
+ os.mkdir(self.media_store_path)
1639
+ config["media_store_path"] = self.media_store_path
1640
+
1641
+ provider_config = {
1642
+ "module": "synapse.media.storage_provider.FileStorageProviderBackend",
1643
+ "store_local": True,
1644
+ "store_synchronous": False,
1645
+ "store_remote": True,
1646
+ "config": {"directory": self.storage_path},
1647
+ }
1648
+
1649
+ config["media_storage_providers"] = [provider_config]
1650
+
1651
+ return self.setup_test_homeserver(config=config)
1652
+
1653
+ def prepare(self, reactor: MemoryReactor, clock: Clock, hs: HomeServer) -> None:
1654
+ self.user = self.register_user("user", "password")
1655
+ self.tok = self.login("user", "password")
1656
+
1657
+ hs.get_module_api().register_media_repository_callbacks(
1658
+ get_media_config_for_user=self.get_media_config_for_user,
1659
+ )
1660
+
1661
+ async def get_media_config_for_user(
1662
+ self,
1663
+ user_id: str,
1664
+ ) -> JsonDict | None:
1665
+ # We echo back the user_id and set a custom upload size.
1666
+ return {"m.upload.size": 1024, "user_id": user_id}
1667
+
1668
+ def test_media_config(self) -> None:
1669
+ channel = self.make_request(
1670
+ "GET",
1671
+ "/_matrix/client/v1/media/config",
1672
+ shorthand=False,
1673
+ access_token=self.tok,
1674
+ )
1675
+ self.assertEqual(channel.code, 200)
1676
+ self.assertEqual(channel.json_body["m.upload.size"], 1024)
1677
+ self.assertEqual(channel.json_body["user_id"], self.user)
1678
+
1679
+
1680
+ class RemoteDownloadLimiterTestCase(unittest.HomeserverTestCase):
1681
+ servlets = [
1682
+ media.register_servlets,
1683
+ login.register_servlets,
1684
+ admin.register_servlets,
1685
+ ]
1686
+
1687
+ def make_homeserver(self, reactor: MemoryReactor, clock: Clock) -> HomeServer:
1688
+ config = self.default_config()
1689
+
1690
+ self.storage_path = self.mktemp()
1691
+ self.media_store_path = self.mktemp()
1692
+ os.mkdir(self.storage_path)
1693
+ os.mkdir(self.media_store_path)
1694
+ config["media_store_path"] = self.media_store_path
1695
+
1696
+ provider_config = {
1697
+ "module": "synapse.media.storage_provider.FileStorageProviderBackend",
1698
+ "store_local": True,
1699
+ "store_synchronous": False,
1700
+ "store_remote": True,
1701
+ "config": {"directory": self.storage_path},
1702
+ }
1703
+
1704
+ config["media_storage_providers"] = [provider_config]
1705
+
1706
+ return self.setup_test_homeserver(config=config)
1707
+
1708
+ def prepare(self, reactor: MemoryReactor, clock: Clock, hs: HomeServer) -> None:
1709
+ self.repo = hs.get_media_repository()
1710
+ self.client = hs.get_federation_http_client()
1711
+ self.store = hs.get_datastores().main
1712
+ self.user = self.register_user("user", "pass")
1713
+ self.tok = self.login("user", "pass")
1714
+
1715
+ # mock actually reading file body
1716
+ def read_multipart_response_30MiB(*args: Any, **kwargs: Any) -> Deferred:
1717
+ d: Deferred = defer.Deferred()
1718
+ d.callback(MultipartResponse(b"{}", 31457280, b"img/png", None))
1719
+ return d
1720
+
1721
+ def read_multipart_response_50MiB(*args: Any, **kwargs: Any) -> Deferred:
1722
+ d: Deferred = defer.Deferred()
1723
+ d.callback(MultipartResponse(b"{}", 31457280, b"img/png", None))
1724
+ return d
1725
+
1726
+ @patch(
1727
+ "synapse.http.matrixfederationclient.read_multipart_response",
1728
+ read_multipart_response_30MiB,
1729
+ )
1730
+ def test_download_ratelimit_default(self) -> None:
1731
+ """
1732
+ Test remote media download ratelimiting against default configuration - 500MB bucket
1733
+ and 87kb/second drain rate
1734
+ """
1735
+
1736
+ # mock out actually sending the request, returns a 30MiB response
1737
+ async def _send_request(*args: Any, **kwargs: Any) -> IResponse:
1738
+ resp = MagicMock(spec=IResponse)
1739
+ resp.code = 200
1740
+ resp.length = 31457280
1741
+ resp.headers = Headers(
1742
+ {"Content-Type": ["multipart/mixed; boundary=gc0p4Jq0M2Yt08jU534c0p"]}
1743
+ )
1744
+ resp.phrase = b"OK"
1745
+ return resp
1746
+
1747
+ self.client._send_request = _send_request # type: ignore
1748
+
1749
+ # first request should go through
1750
+ channel = self.make_request(
1751
+ "GET",
1752
+ "/_matrix/client/v1/media/download/remote.org/abc",
1753
+ shorthand=False,
1754
+ access_token=self.tok,
1755
+ )
1756
+ assert channel.code == 200
1757
+
1758
+ # next 15 should go through
1759
+ for i in range(15):
1760
+ channel2 = self.make_request(
1761
+ "GET",
1762
+ f"/_matrix/client/v1/media/download/remote.org/abc{i}",
1763
+ shorthand=False,
1764
+ access_token=self.tok,
1765
+ )
1766
+ assert channel2.code == 200
1767
+
1768
+ # 17th will hit ratelimit
1769
+ channel3 = self.make_request(
1770
+ "GET",
1771
+ "/_matrix/client/v1/media/download/remote.org/abcd",
1772
+ shorthand=False,
1773
+ access_token=self.tok,
1774
+ )
1775
+ assert channel3.code == 429
1776
+
1777
+ # however, a request from a different IP will go through
1778
+ channel4 = self.make_request(
1779
+ "GET",
1780
+ "/_matrix/client/v1/media/download/remote.org/abcde",
1781
+ shorthand=False,
1782
+ client_ip="187.233.230.159",
1783
+ access_token=self.tok,
1784
+ )
1785
+ assert channel4.code == 200
1786
+
1787
+ # at 87Kib/s it should take about 2 minutes for enough to drain from bucket that another
1788
+ # 30MiB download is authorized - The last download was blocked at 503,316,480.
1789
+ # The next download will be authorized when bucket hits 492,830,720
1790
+ # (524,288,000 total capacity - 31,457,280 download size) so 503,316,480 - 492,830,720 ~= 10,485,760
1791
+ # needs to drain before another download will be authorized, that will take ~=
1792
+ # 2 minutes (10,485,760/89,088/60)
1793
+ self.reactor.pump([2.0 * 60.0])
1794
+
1795
+ # enough has drained and next request goes through
1796
+ channel5 = self.make_request(
1797
+ "GET",
1798
+ "/_matrix/client/v1/media/download/remote.org/abcdef",
1799
+ shorthand=False,
1800
+ access_token=self.tok,
1801
+ )
1802
+ assert channel5.code == 200
1803
+
1804
+ @override_config(
1805
+ {
1806
+ "remote_media_download_per_second": "50M",
1807
+ "remote_media_download_burst_count": "50M",
1808
+ }
1809
+ )
1810
+ @patch(
1811
+ "synapse.http.matrixfederationclient.read_multipart_response",
1812
+ read_multipart_response_50MiB,
1813
+ )
1814
+ def test_download_rate_limit_config(self) -> None:
1815
+ """
1816
+ Test that download rate limit config options are correctly picked up and applied
1817
+ """
1818
+
1819
+ async def _send_request(*args: Any, **kwargs: Any) -> IResponse:
1820
+ resp = MagicMock(spec=IResponse)
1821
+ resp.code = 200
1822
+ resp.length = 52428800
1823
+ resp.headers = Headers(
1824
+ {"Content-Type": ["multipart/mixed; boundary=gc0p4Jq0M2Yt08jU534c0p"]}
1825
+ )
1826
+ resp.phrase = b"OK"
1827
+ return resp
1828
+
1829
+ self.client._send_request = _send_request # type: ignore
1830
+
1831
+ # first request should go through
1832
+ channel = self.make_request(
1833
+ "GET",
1834
+ "/_matrix/client/v1/media/download/remote.org/abc",
1835
+ shorthand=False,
1836
+ access_token=self.tok,
1837
+ )
1838
+ assert channel.code == 200
1839
+
1840
+ # immediate second request should fail
1841
+ channel = self.make_request(
1842
+ "GET",
1843
+ "/_matrix/client/v1/media/download/remote.org/abcd",
1844
+ shorthand=False,
1845
+ access_token=self.tok,
1846
+ )
1847
+ assert channel.code == 429
1848
+
1849
+ # advance half a second
1850
+ self.reactor.pump([0.5])
1851
+
1852
+ # request still fails
1853
+ channel = self.make_request(
1854
+ "GET",
1855
+ "/_matrix/client/v1/media/download/remote.org/abcde",
1856
+ shorthand=False,
1857
+ access_token=self.tok,
1858
+ )
1859
+ assert channel.code == 429
1860
+
1861
+ # advance another half second
1862
+ self.reactor.pump([0.5])
1863
+
1864
+ # enough has drained from bucket and request is successful
1865
+ channel = self.make_request(
1866
+ "GET",
1867
+ "/_matrix/client/v1/media/download/remote.org/abcdef",
1868
+ shorthand=False,
1869
+ access_token=self.tok,
1870
+ )
1871
+ assert channel.code == 200
1872
+
1873
+ @override_config(
1874
+ {
1875
+ "remote_media_download_burst_count": "87M",
1876
+ }
1877
+ )
1878
+ @patch(
1879
+ "synapse.http.matrixfederationclient.read_multipart_response",
1880
+ read_multipart_response_30MiB,
1881
+ )
1882
+ def test_download_ratelimit_unknown_length(self) -> None:
1883
+ """
1884
+ Test that if no content-length is provided, ratelimiting is still applied after
1885
+ media is downloaded and length is known
1886
+ """
1887
+
1888
+ # mock out actually sending the request
1889
+ async def _send_request(*args: Any, **kwargs: Any) -> IResponse:
1890
+ resp = MagicMock(spec=IResponse)
1891
+ resp.code = 200
1892
+ resp.length = UNKNOWN_LENGTH
1893
+ resp.headers = Headers(
1894
+ {"Content-Type": ["multipart/mixed; boundary=gc0p4Jq0M2Yt08jU534c0p"]}
1895
+ )
1896
+ resp.phrase = b"OK"
1897
+ return resp
1898
+
1899
+ self.client._send_request = _send_request # type: ignore
1900
+
1901
+ # first 3 will go through (note that 3rd request technically violates rate limit but
1902
+ # that since the ratelimiting is applied *after* download it goes through, but next one fails)
1903
+ for i in range(3):
1904
+ channel2 = self.make_request(
1905
+ "GET",
1906
+ f"/_matrix/client/v1/media/download/remote.org/abc{i}",
1907
+ shorthand=False,
1908
+ access_token=self.tok,
1909
+ )
1910
+ assert channel2.code == 200
1911
+
1912
+ # 4th will hit ratelimit
1913
+ channel3 = self.make_request(
1914
+ "GET",
1915
+ "/_matrix/client/v1/media/download/remote.org/abcd",
1916
+ shorthand=False,
1917
+ access_token=self.tok,
1918
+ )
1919
+ assert channel3.code == 429
1920
+
1921
+ @override_config({"max_upload_size": "29M"})
1922
+ @patch(
1923
+ "synapse.http.matrixfederationclient.read_multipart_response",
1924
+ read_multipart_response_30MiB,
1925
+ )
1926
+ def test_max_download_respected(self) -> None:
1927
+ """
1928
+ Test that the max download size is enforced - note that max download size is determined
1929
+ by the max_upload_size
1930
+ """
1931
+
1932
+ # mock out actually sending the request, returns a 30MiB response
1933
+ async def _send_request(*args: Any, **kwargs: Any) -> IResponse:
1934
+ resp = MagicMock(spec=IResponse)
1935
+ resp.code = 200
1936
+ resp.length = 31457280
1937
+ resp.headers = Headers(
1938
+ {"Content-Type": ["multipart/mixed; boundary=gc0p4Jq0M2Yt08jU534c0p"]}
1939
+ )
1940
+ resp.phrase = b"OK"
1941
+ return resp
1942
+
1943
+ self.client._send_request = _send_request # type: ignore
1944
+
1945
+ channel = self.make_request(
1946
+ "GET",
1947
+ "/_matrix/client/v1/media/download/remote.org/abcd",
1948
+ shorthand=False,
1949
+ access_token=self.tok,
1950
+ )
1951
+ assert channel.code == 502
1952
+ assert channel.json_body["errcode"] == "M_TOO_LARGE"
1953
+
1954
+ def test_file_download(self) -> None:
1955
+ content = io.BytesIO(b"file_to_stream")
1956
+ content_uri = self.get_success(
1957
+ self.repo.create_or_update_content(
1958
+ "text/plain",
1959
+ "test_upload",
1960
+ content,
1961
+ 46,
1962
+ UserID.from_string("@user_id:whatever.org"),
1963
+ )
1964
+ )
1965
+ # test with a text file
1966
+ channel = self.make_request(
1967
+ "GET",
1968
+ f"/_matrix/client/v1/media/download/test/{content_uri.media_id}",
1969
+ shorthand=False,
1970
+ access_token=self.tok,
1971
+ )
1972
+ self.pump()
1973
+ self.assertEqual(200, channel.code)
1974
+
1975
+
1976
+ test_images = [
1977
+ small_png,
1978
+ small_png_with_transparency,
1979
+ small_cmyk_jpeg,
1980
+ small_lossless_webp,
1981
+ empty_file,
1982
+ SVG,
1983
+ ]
1984
+ input_values = [(x,) for x in test_images]
1985
+
1986
+
1987
+ @parameterized_class(("test_image",), input_values)
1988
+ class DownloadAndThumbnailTestCase(unittest.HomeserverTestCase):
1989
+ test_image: ClassVar[TestImage]
1990
+ servlets = [
1991
+ media.register_servlets,
1992
+ login.register_servlets,
1993
+ admin.register_servlets,
1994
+ ]
1995
+
1996
+ def make_homeserver(self, reactor: MemoryReactor, clock: Clock) -> HomeServer:
1997
+ self.fetches: list[
1998
+ tuple[
1999
+ "Deferred[Any]",
2000
+ str,
2001
+ str,
2002
+ QueryParams | None,
2003
+ ]
2004
+ ] = []
2005
+
2006
+ def federation_get_file(
2007
+ destination: str,
2008
+ path: str,
2009
+ output_stream: BinaryIO,
2010
+ download_ratelimiter: Ratelimiter,
2011
+ ip_address: Any,
2012
+ max_size: int,
2013
+ args: QueryParams | None = None,
2014
+ retry_on_dns_fail: bool = True,
2015
+ ignore_backoff: bool = False,
2016
+ follow_redirects: bool = False,
2017
+ ) -> "Deferred[tuple[int, dict[bytes, list[bytes]], bytes]]":
2018
+ """A mock for MatrixFederationHttpClient.federation_get_file."""
2019
+
2020
+ def write_to(
2021
+ r: tuple[bytes, tuple[int, dict[bytes, list[bytes]], bytes]],
2022
+ ) -> tuple[int, dict[bytes, list[bytes]], bytes]:
2023
+ data, response = r
2024
+ output_stream.write(data)
2025
+ return response
2026
+
2027
+ def write_err(f: Failure) -> Failure:
2028
+ f.trap(HttpResponseException)
2029
+ output_stream.write(f.value.response)
2030
+ return f
2031
+
2032
+ d: Deferred[tuple[bytes, tuple[int, dict[bytes, list[bytes]], bytes]]] = (
2033
+ Deferred()
2034
+ )
2035
+ self.fetches.append((d, destination, path, args))
2036
+ # Note that this callback changes the value held by d.
2037
+ d_after_callback = d.addCallbacks(write_to, write_err)
2038
+ return make_deferred_yieldable(d_after_callback)
2039
+
2040
+ def get_file(
2041
+ destination: str,
2042
+ path: str,
2043
+ output_stream: BinaryIO,
2044
+ download_ratelimiter: Ratelimiter,
2045
+ ip_address: Any,
2046
+ max_size: int,
2047
+ args: QueryParams | None = None,
2048
+ retry_on_dns_fail: bool = True,
2049
+ ignore_backoff: bool = False,
2050
+ follow_redirects: bool = False,
2051
+ ) -> "Deferred[tuple[int, dict[bytes, list[bytes]]]]":
2052
+ """A mock for MatrixFederationHttpClient.get_file."""
2053
+
2054
+ def write_to(
2055
+ r: tuple[bytes, tuple[int, dict[bytes, list[bytes]]]],
2056
+ ) -> tuple[int, dict[bytes, list[bytes]]]:
2057
+ data, response = r
2058
+ output_stream.write(data)
2059
+ return response
2060
+
2061
+ def write_err(f: Failure) -> Failure:
2062
+ f.trap(HttpResponseException)
2063
+ output_stream.write(f.value.response)
2064
+ return f
2065
+
2066
+ d: Deferred[tuple[bytes, tuple[int, dict[bytes, list[bytes]]]]] = Deferred()
2067
+ self.fetches.append((d, destination, path, args))
2068
+ # Note that this callback changes the value held by d.
2069
+ d_after_callback = d.addCallbacks(write_to, write_err)
2070
+ return make_deferred_yieldable(d_after_callback)
2071
+
2072
+ # Mock out the homeserver's MatrixFederationHttpClient
2073
+ client = Mock()
2074
+ client.federation_get_file = federation_get_file
2075
+ client.get_file = get_file
2076
+
2077
+ self.storage_path = self.mktemp()
2078
+ self.media_store_path = self.mktemp()
2079
+ os.mkdir(self.storage_path)
2080
+ os.mkdir(self.media_store_path)
2081
+
2082
+ config = self.default_config()
2083
+ config["media_store_path"] = self.media_store_path
2084
+ config["max_image_pixels"] = 2000000
2085
+
2086
+ provider_config = {
2087
+ "module": "synapse.media.storage_provider.FileStorageProviderBackend",
2088
+ "store_local": True,
2089
+ "store_synchronous": False,
2090
+ "store_remote": True,
2091
+ "config": {"directory": self.storage_path},
2092
+ }
2093
+ config["media_storage_providers"] = [provider_config]
2094
+
2095
+ hs = self.setup_test_homeserver(config=config, federation_http_client=client)
2096
+
2097
+ return hs
2098
+
2099
+ def prepare(self, reactor: MemoryReactor, clock: Clock, hs: HomeServer) -> None:
2100
+ self.store = hs.get_datastores().main
2101
+ self.media_repo = hs.get_media_repository()
2102
+
2103
+ self.remote = "example.com"
2104
+ self.media_id = "12345"
2105
+
2106
+ self.user = self.register_user("user", "pass")
2107
+ self.tok = self.login("user", "pass")
2108
+
2109
+ def _req(
2110
+ self, content_disposition: bytes | None, include_content_type: bool = True
2111
+ ) -> FakeChannel:
2112
+ channel = self.make_request(
2113
+ "GET",
2114
+ f"/_matrix/client/v1/media/download/{self.remote}/{self.media_id}",
2115
+ shorthand=False,
2116
+ await_result=False,
2117
+ access_token=self.tok,
2118
+ )
2119
+ self.pump()
2120
+
2121
+ # We've made one fetch, to example.com, using the federation media URL
2122
+ self.assertEqual(len(self.fetches), 1)
2123
+ self.assertEqual(self.fetches[0][1], "example.com")
2124
+ self.assertEqual(
2125
+ self.fetches[0][2], "/_matrix/federation/v1/media/download/" + self.media_id
2126
+ )
2127
+ self.assertEqual(
2128
+ self.fetches[0][3],
2129
+ {"timeout_ms": "20000"},
2130
+ )
2131
+
2132
+ headers = {
2133
+ b"Content-Length": [b"%d" % (len(self.test_image.data))],
2134
+ }
2135
+
2136
+ if include_content_type:
2137
+ headers[b"Content-Type"] = [self.test_image.content_type]
2138
+
2139
+ if content_disposition:
2140
+ headers[b"Content-Disposition"] = [content_disposition]
2141
+
2142
+ self.fetches[0][0].callback(
2143
+ (self.test_image.data, (len(self.test_image.data), headers, b"{}"))
2144
+ )
2145
+
2146
+ self.pump()
2147
+ self.assertEqual(channel.code, 200)
2148
+
2149
+ return channel
2150
+
2151
+ def test_handle_missing_content_type(self) -> None:
2152
+ channel = self._req(
2153
+ b"attachment; filename=out" + self.test_image.extension,
2154
+ include_content_type=False,
2155
+ )
2156
+ headers = channel.headers
2157
+ self.assertEqual(channel.code, 200)
2158
+ self.assertEqual(
2159
+ headers.getRawHeaders(b"Content-Type"), [b"application/octet-stream"]
2160
+ )
2161
+
2162
+ def test_disposition_filename_ascii(self) -> None:
2163
+ """
2164
+ If the filename is filename=<ascii> then Synapse will decode it as an
2165
+ ASCII string, and use filename= in the response.
2166
+ """
2167
+ channel = self._req(b"attachment; filename=out" + self.test_image.extension)
2168
+
2169
+ headers = channel.headers
2170
+ self.assertEqual(
2171
+ headers.getRawHeaders(b"Content-Type"), [self.test_image.content_type]
2172
+ )
2173
+ self.assertEqual(
2174
+ headers.getRawHeaders(b"Content-Disposition"),
2175
+ [
2176
+ (b"inline" if self.test_image.is_inline else b"attachment")
2177
+ + b"; filename=out"
2178
+ + self.test_image.extension
2179
+ ],
2180
+ )
2181
+
2182
+ def test_disposition_filenamestar_utf8escaped(self) -> None:
2183
+ """
2184
+ If the filename is filename=*utf8''<utf8 escaped> then Synapse will
2185
+ correctly decode it as the UTF-8 string, and use filename* in the
2186
+ response.
2187
+ """
2188
+ filename = parse.quote("\u2603".encode()).encode("ascii")
2189
+ channel = self._req(
2190
+ b"attachment; filename*=utf-8''" + filename + self.test_image.extension
2191
+ )
2192
+
2193
+ headers = channel.headers
2194
+ self.assertEqual(
2195
+ headers.getRawHeaders(b"Content-Type"), [self.test_image.content_type]
2196
+ )
2197
+ self.assertEqual(
2198
+ headers.getRawHeaders(b"Content-Disposition"),
2199
+ [
2200
+ (b"inline" if self.test_image.is_inline else b"attachment")
2201
+ + b"; filename*=utf-8''"
2202
+ + filename
2203
+ + self.test_image.extension
2204
+ ],
2205
+ )
2206
+
2207
+ def test_disposition_none(self) -> None:
2208
+ """
2209
+ If there is no filename, Content-Disposition should only
2210
+ be a disposition type.
2211
+ """
2212
+ channel = self._req(None)
2213
+
2214
+ headers = channel.headers
2215
+ self.assertEqual(
2216
+ headers.getRawHeaders(b"Content-Type"), [self.test_image.content_type]
2217
+ )
2218
+ self.assertEqual(
2219
+ headers.getRawHeaders(b"Content-Disposition"),
2220
+ [b"inline" if self.test_image.is_inline else b"attachment"],
2221
+ )
2222
+
2223
+ def test_x_robots_tag_header(self) -> None:
2224
+ """
2225
+ Tests that the `X-Robots-Tag` header is present, which informs web crawlers
2226
+ to not index, archive, or follow links in media.
2227
+ """
2228
+ channel = self._req(b"attachment; filename=out" + self.test_image.extension)
2229
+
2230
+ headers = channel.headers
2231
+ self.assertEqual(
2232
+ headers.getRawHeaders(b"X-Robots-Tag"),
2233
+ [b"noindex, nofollow, noarchive, noimageindex"],
2234
+ )
2235
+
2236
+ def test_cross_origin_resource_policy_header(self) -> None:
2237
+ """
2238
+ Test that the Cross-Origin-Resource-Policy header is set to "cross-origin"
2239
+ allowing web clients to embed media from the downloads API.
2240
+ """
2241
+ channel = self._req(b"attachment; filename=out" + self.test_image.extension)
2242
+
2243
+ headers = channel.headers
2244
+
2245
+ self.assertEqual(
2246
+ headers.getRawHeaders(b"Cross-Origin-Resource-Policy"),
2247
+ [b"cross-origin"],
2248
+ )
2249
+
2250
+ def test_unknown_federation_endpoint(self) -> None:
2251
+ """
2252
+ Test that if the download request to remote federation endpoint returns a 404
2253
+ we fall back to the _matrix/media endpoint
2254
+ """
2255
+ channel = self.make_request(
2256
+ "GET",
2257
+ f"/_matrix/client/v1/media/download/{self.remote}/{self.media_id}",
2258
+ shorthand=False,
2259
+ await_result=False,
2260
+ access_token=self.tok,
2261
+ )
2262
+ self.pump()
2263
+
2264
+ # We've made one fetch, to example.com, using the media URL, and asking
2265
+ # the other server not to do a remote fetch
2266
+ self.assertEqual(len(self.fetches), 1)
2267
+ self.assertEqual(self.fetches[0][1], "example.com")
2268
+ self.assertEqual(
2269
+ self.fetches[0][2], f"/_matrix/federation/v1/media/download/{self.media_id}"
2270
+ )
2271
+
2272
+ # The result which says the endpoint is unknown.
2273
+ unknown_endpoint = b'{"errcode":"M_UNRECOGNIZED","error":"Unknown request"}'
2274
+ self.fetches[0][0].errback(
2275
+ HttpResponseException(404, "NOT FOUND", unknown_endpoint)
2276
+ )
2277
+
2278
+ self.pump()
2279
+
2280
+ # There should now be another request to the _matrix/media/v3/download URL.
2281
+ self.assertEqual(len(self.fetches), 2)
2282
+ self.assertEqual(self.fetches[1][1], "example.com")
2283
+ self.assertEqual(
2284
+ self.fetches[1][2],
2285
+ f"/_matrix/media/v3/download/example.com/{self.media_id}",
2286
+ )
2287
+
2288
+ headers = {
2289
+ b"Content-Length": [b"%d" % (len(self.test_image.data))],
2290
+ }
2291
+
2292
+ self.fetches[1][0].callback(
2293
+ (self.test_image.data, (len(self.test_image.data), headers))
2294
+ )
2295
+
2296
+ self.pump()
2297
+ self.assertEqual(channel.code, 200)
2298
+
2299
+ def test_thumbnail_crop(self) -> None:
2300
+ """Test that a cropped remote thumbnail is available."""
2301
+ self._test_thumbnail(
2302
+ "crop",
2303
+ self.test_image.expected_cropped,
2304
+ expected_found=self.test_image.expected_found,
2305
+ unable_to_thumbnail=self.test_image.unable_to_thumbnail,
2306
+ )
2307
+
2308
+ def test_thumbnail_scale(self) -> None:
2309
+ """Test that a scaled remote thumbnail is available."""
2310
+ self._test_thumbnail(
2311
+ "scale",
2312
+ self.test_image.expected_scaled,
2313
+ expected_found=self.test_image.expected_found,
2314
+ unable_to_thumbnail=self.test_image.unable_to_thumbnail,
2315
+ )
2316
+
2317
+ def test_invalid_type(self) -> None:
2318
+ """An invalid thumbnail type is never available."""
2319
+ self._test_thumbnail(
2320
+ "invalid",
2321
+ None,
2322
+ expected_found=False,
2323
+ unable_to_thumbnail=self.test_image.unable_to_thumbnail,
2324
+ )
2325
+
2326
+ @unittest.override_config(
2327
+ {"thumbnail_sizes": [{"width": 32, "height": 32, "method": "scale"}]}
2328
+ )
2329
+ def test_no_thumbnail_crop(self) -> None:
2330
+ """
2331
+ Override the config to generate only scaled thumbnails, but request a cropped one.
2332
+ """
2333
+ self._test_thumbnail(
2334
+ "crop",
2335
+ None,
2336
+ expected_found=False,
2337
+ unable_to_thumbnail=self.test_image.unable_to_thumbnail,
2338
+ )
2339
+
2340
+ @unittest.override_config(
2341
+ {"thumbnail_sizes": [{"width": 32, "height": 32, "method": "crop"}]}
2342
+ )
2343
+ def test_no_thumbnail_scale(self) -> None:
2344
+ """
2345
+ Override the config to generate only cropped thumbnails, but request a scaled one.
2346
+ """
2347
+ self._test_thumbnail(
2348
+ "scale",
2349
+ None,
2350
+ expected_found=False,
2351
+ unable_to_thumbnail=self.test_image.unable_to_thumbnail,
2352
+ )
2353
+
2354
+ def test_thumbnail_repeated_thumbnail(self) -> None:
2355
+ """Test that fetching the same thumbnail works, and deleting the on disk
2356
+ thumbnail regenerates it.
2357
+ """
2358
+ self._test_thumbnail(
2359
+ "scale",
2360
+ self.test_image.expected_scaled,
2361
+ expected_found=self.test_image.expected_found,
2362
+ unable_to_thumbnail=self.test_image.unable_to_thumbnail,
2363
+ )
2364
+
2365
+ if not self.test_image.expected_found:
2366
+ return
2367
+
2368
+ # Fetching again should work, without re-requesting the image from the
2369
+ # remote.
2370
+ params = "?width=32&height=32&method=scale"
2371
+ channel = self.make_request(
2372
+ "GET",
2373
+ f"/_matrix/client/v1/media/thumbnail/{self.remote}/{self.media_id}{params}",
2374
+ shorthand=False,
2375
+ await_result=False,
2376
+ access_token=self.tok,
2377
+ )
2378
+ self.pump()
2379
+
2380
+ self.assertEqual(channel.code, 200)
2381
+ if self.test_image.expected_scaled:
2382
+ self.assertEqual(
2383
+ channel.result["body"],
2384
+ self.test_image.expected_scaled,
2385
+ channel.result["body"],
2386
+ )
2387
+
2388
+ # Deleting the thumbnail on disk then re-requesting it should work as
2389
+ # Synapse should regenerate missing thumbnails.
2390
+ info = self.get_success(
2391
+ self.store.get_cached_remote_media(self.remote, self.media_id)
2392
+ )
2393
+ assert info is not None
2394
+ file_id = info.filesystem_id
2395
+
2396
+ thumbnail_dir = self.media_repo.filepaths.remote_media_thumbnail_dir(
2397
+ self.remote, file_id
2398
+ )
2399
+ shutil.rmtree(thumbnail_dir, ignore_errors=True)
2400
+
2401
+ channel = self.make_request(
2402
+ "GET",
2403
+ f"/_matrix/client/v1/media/thumbnail/{self.remote}/{self.media_id}{params}",
2404
+ shorthand=False,
2405
+ await_result=False,
2406
+ access_token=self.tok,
2407
+ )
2408
+ self.pump()
2409
+
2410
+ self.assertEqual(channel.code, 200)
2411
+ if self.test_image.expected_scaled:
2412
+ self.assertEqual(
2413
+ channel.result["body"],
2414
+ self.test_image.expected_scaled,
2415
+ channel.result["body"],
2416
+ )
2417
+
2418
+ def _test_thumbnail(
2419
+ self,
2420
+ method: str,
2421
+ expected_body: bytes | None,
2422
+ expected_found: bool,
2423
+ unable_to_thumbnail: bool = False,
2424
+ ) -> None:
2425
+ """Test the given thumbnailing method works as expected.
2426
+
2427
+ Args:
2428
+ method: The thumbnailing method to use (crop, scale).
2429
+ expected_body: The expected bytes from thumbnailing, or None if
2430
+ test should just check for a valid image.
2431
+ expected_found: True if the file should exist on the server, or False if
2432
+ a 404/400 is expected.
2433
+ unable_to_thumbnail: True if we expect the thumbnailing to fail (400), or
2434
+ False if the thumbnailing should succeed or a normal 404 is expected.
2435
+ """
2436
+
2437
+ params = "?width=32&height=32&method=" + method
2438
+ channel = self.make_request(
2439
+ "GET",
2440
+ f"/_matrix/client/v1/media/thumbnail/{self.remote}/{self.media_id}{params}",
2441
+ shorthand=False,
2442
+ await_result=False,
2443
+ access_token=self.tok,
2444
+ )
2445
+ self.pump()
2446
+ headers = {
2447
+ b"Content-Length": [b"%d" % (len(self.test_image.data))],
2448
+ b"Content-Type": [self.test_image.content_type],
2449
+ }
2450
+ self.fetches[0][0].callback(
2451
+ (self.test_image.data, (len(self.test_image.data), headers))
2452
+ )
2453
+ self.pump()
2454
+ if expected_found:
2455
+ self.assertEqual(channel.code, 200)
2456
+
2457
+ self.assertEqual(
2458
+ channel.headers.getRawHeaders(b"Cross-Origin-Resource-Policy"),
2459
+ [b"cross-origin"],
2460
+ )
2461
+
2462
+ if expected_body is not None:
2463
+ self.assertEqual(
2464
+ channel.result["body"], expected_body, channel.result["body"].hex()
2465
+ )
2466
+ else:
2467
+ # ensure that the result is at least some valid image
2468
+ Image.open(io.BytesIO(channel.result["body"]))
2469
+ elif unable_to_thumbnail:
2470
+ # A 400 with a JSON body.
2471
+ self.assertEqual(channel.code, 400)
2472
+ self.assertEqual(
2473
+ channel.json_body,
2474
+ {
2475
+ "errcode": "M_UNKNOWN",
2476
+ "error": "Cannot find any thumbnails for the requested media ('/_matrix/client/v1/media/thumbnail/example.com/12345'). This might mean the media is not a supported_media_format=(image/jpeg, image/jpg, image/webp, image/gif, image/png) or that thumbnailing failed for some other reason. (Dynamic thumbnails are disabled on this server.)",
2477
+ },
2478
+ )
2479
+ else:
2480
+ # A 404 with a JSON body.
2481
+ self.assertEqual(channel.code, 404)
2482
+ self.assertEqual(
2483
+ channel.json_body,
2484
+ {
2485
+ "errcode": "M_NOT_FOUND",
2486
+ "error": "Not found '/_matrix/client/v1/media/thumbnail/example.com/12345'",
2487
+ },
2488
+ )
2489
+
2490
+ @parameterized.expand([("crop", 16), ("crop", 64), ("scale", 16), ("scale", 64)])
2491
+ def test_same_quality(self, method: str, desired_size: int) -> None:
2492
+ """Test that choosing between thumbnails with the same quality rating succeeds.
2493
+
2494
+ We are not particular about which thumbnail is chosen."""
2495
+
2496
+ content_type = self.test_image.content_type.decode()
2497
+ media_repo = self.hs.get_media_repository()
2498
+ thumbnail_provider = ThumbnailProvider(
2499
+ self.hs, media_repo, media_repo.media_storage
2500
+ )
2501
+
2502
+ self.assertIsNotNone(
2503
+ thumbnail_provider._select_thumbnail(
2504
+ desired_width=desired_size,
2505
+ desired_height=desired_size,
2506
+ desired_method=method,
2507
+ desired_type=content_type,
2508
+ # Provide two identical thumbnails which are guaranteed to have the same
2509
+ # quality rating.
2510
+ thumbnail_infos=[
2511
+ ThumbnailInfo(
2512
+ width=32,
2513
+ height=32,
2514
+ method=method,
2515
+ type=content_type,
2516
+ length=256,
2517
+ ),
2518
+ ThumbnailInfo(
2519
+ width=32,
2520
+ height=32,
2521
+ method=method,
2522
+ type=content_type,
2523
+ length=256,
2524
+ ),
2525
+ ],
2526
+ file_id=f"image{self.test_image.extension.decode()}",
2527
+ url_cache=False,
2528
+ server_name=None,
2529
+ )
2530
+ )
2531
+
2532
+
2533
+ configs = [
2534
+ {"extra_config": {"dynamic_thumbnails": True}},
2535
+ {"extra_config": {"dynamic_thumbnails": False}},
2536
+ ]
2537
+
2538
+
2539
+ @parameterized_class(configs)
2540
+ class AuthenticatedMediaTestCase(unittest.HomeserverTestCase):
2541
+ extra_config: dict[str, Any]
2542
+ servlets = [
2543
+ media.register_servlets,
2544
+ login.register_servlets,
2545
+ admin.register_servlets,
2546
+ ]
2547
+
2548
+ def make_homeserver(self, reactor: MemoryReactor, clock: Clock) -> HomeServer:
2549
+ config = self.default_config()
2550
+
2551
+ self.clock = clock
2552
+ self.storage_path = self.mktemp()
2553
+ self.media_store_path = self.mktemp()
2554
+ os.mkdir(self.storage_path)
2555
+ os.mkdir(self.media_store_path)
2556
+ config["media_store_path"] = self.media_store_path
2557
+ config["enable_authenticated_media"] = True
2558
+
2559
+ provider_config = {
2560
+ "module": "synapse.media.storage_provider.FileStorageProviderBackend",
2561
+ "store_local": True,
2562
+ "store_synchronous": False,
2563
+ "store_remote": True,
2564
+ "config": {"directory": self.storage_path},
2565
+ }
2566
+
2567
+ config["media_storage_providers"] = [provider_config]
2568
+ config.update(self.extra_config)
2569
+
2570
+ return self.setup_test_homeserver(config=config)
2571
+
2572
+ def prepare(self, reactor: MemoryReactor, clock: Clock, hs: HomeServer) -> None:
2573
+ self.repo = hs.get_media_repository()
2574
+ self.client = hs.get_federation_http_client()
2575
+ self.store = hs.get_datastores().main
2576
+ self.user = self.register_user("user", "pass")
2577
+ self.tok = self.login("user", "pass")
2578
+
2579
+ def create_resource_dict(self) -> dict[str, Resource]:
2580
+ resources = super().create_resource_dict()
2581
+ resources["/_matrix/media"] = self.hs.get_media_repository_resource()
2582
+ return resources
2583
+
2584
+ def test_authenticated_media(self) -> None:
2585
+ # upload some local media with authentication on
2586
+ channel = self.make_request(
2587
+ "POST",
2588
+ "_matrix/media/v3/upload?filename=test_png_upload",
2589
+ SMALL_PNG,
2590
+ self.tok,
2591
+ shorthand=False,
2592
+ content_type=b"image/png",
2593
+ )
2594
+ self.assertEqual(channel.code, 200)
2595
+ res = channel.json_body.get("content_uri")
2596
+ assert res is not None
2597
+ uri = res.split("mxc://")[1]
2598
+
2599
+ # request media over authenticated endpoint, should be found
2600
+ channel2 = self.make_request(
2601
+ "GET",
2602
+ f"_matrix/client/v1/media/download/{uri}",
2603
+ access_token=self.tok,
2604
+ shorthand=False,
2605
+ )
2606
+ self.assertEqual(channel2.code, 200)
2607
+
2608
+ # request same media over unauthenticated media, should raise 404 not found
2609
+ channel3 = self.make_request(
2610
+ "GET", f"_matrix/media/v3/download/{uri}", shorthand=False
2611
+ )
2612
+ self.assertEqual(channel3.code, 404)
2613
+
2614
+ # check thumbnails as well
2615
+ params = "?width=32&height=32&method=crop"
2616
+ channel4 = self.make_request(
2617
+ "GET",
2618
+ f"/_matrix/client/v1/media/thumbnail/{uri}{params}",
2619
+ shorthand=False,
2620
+ access_token=self.tok,
2621
+ )
2622
+ self.assertEqual(channel4.code, 200)
2623
+
2624
+ params = "?width=32&height=32&method=crop"
2625
+ channel5 = self.make_request(
2626
+ "GET",
2627
+ f"/_matrix/media/r0/thumbnail/{uri}{params}",
2628
+ shorthand=False,
2629
+ access_token=self.tok,
2630
+ )
2631
+ self.assertEqual(channel5.code, 404)
2632
+
2633
+ # Inject a piece of remote media.
2634
+ file_id = "abcdefg12345"
2635
+ file_info = FileInfo(server_name="lonelyIsland", file_id=file_id)
2636
+
2637
+ media_storage = self.hs.get_media_repository().media_storage
2638
+
2639
+ ctx = media_storage.store_into_file(file_info)
2640
+ (f, fname) = self.get_success(ctx.__aenter__())
2641
+ f.write(SMALL_PNG)
2642
+ self.get_success(ctx.__aexit__(None, None, None))
2643
+
2644
+ # we write the authenticated status when storing media, so this should pick up
2645
+ # config and authenticate the media
2646
+ self.get_success(
2647
+ self.store.store_cached_remote_media(
2648
+ origin="lonelyIsland",
2649
+ media_id="52",
2650
+ media_type="image/png",
2651
+ media_length=1,
2652
+ time_now_ms=self.clock.time_msec(),
2653
+ upload_name="remote_test.png",
2654
+ filesystem_id=file_id,
2655
+ sha256=file_id,
2656
+ )
2657
+ )
2658
+
2659
+ # ensure we have thumbnails for the non-dynamic code path
2660
+ if self.extra_config == {"dynamic_thumbnails": False}:
2661
+ self.get_success(
2662
+ self.repo._generate_thumbnails(
2663
+ "lonelyIsland", "52", file_id, "image/png"
2664
+ )
2665
+ )
2666
+
2667
+ channel6 = self.make_request(
2668
+ "GET",
2669
+ "_matrix/client/v1/media/download/lonelyIsland/52",
2670
+ access_token=self.tok,
2671
+ shorthand=False,
2672
+ )
2673
+ self.assertEqual(channel6.code, 200)
2674
+
2675
+ channel7 = self.make_request(
2676
+ "GET", f"_matrix/media/v3/download/{uri}", shorthand=False
2677
+ )
2678
+ self.assertEqual(channel7.code, 404)
2679
+
2680
+ params = "?width=32&height=32&method=crop"
2681
+ channel8 = self.make_request(
2682
+ "GET",
2683
+ f"/_matrix/client/v1/media/thumbnail/lonelyIsland/52{params}",
2684
+ shorthand=False,
2685
+ access_token=self.tok,
2686
+ )
2687
+ self.assertEqual(channel8.code, 200)
2688
+
2689
+ channel9 = self.make_request(
2690
+ "GET",
2691
+ f"/_matrix/media/r0/thumbnail/lonelyIsland/52{params}",
2692
+ shorthand=False,
2693
+ access_token=self.tok,
2694
+ )
2695
+ self.assertEqual(channel9.code, 404)
2696
+
2697
+ # Inject a piece of local media that isn't authenticated
2698
+ file_id = "abcdefg123456"
2699
+ file_info = FileInfo(None, file_id=file_id)
2700
+
2701
+ ctx = media_storage.store_into_file(file_info)
2702
+ (f, fname) = self.get_success(ctx.__aenter__())
2703
+ f.write(SMALL_PNG)
2704
+ self.get_success(ctx.__aexit__(None, None, None))
2705
+
2706
+ self.get_success(
2707
+ self.store.db_pool.simple_insert(
2708
+ "local_media_repository",
2709
+ {
2710
+ "media_id": "abcdefg123456",
2711
+ "media_type": "image/png",
2712
+ "created_ts": self.clock.time_msec(),
2713
+ "upload_name": "test_local",
2714
+ "media_length": 1,
2715
+ "user_id": "someone",
2716
+ "url_cache": None,
2717
+ "authenticated": False,
2718
+ },
2719
+ desc="store_local_media",
2720
+ )
2721
+ )
2722
+
2723
+ # check that unauthenticated media is still available over both endpoints
2724
+ channel9 = self.make_request(
2725
+ "GET",
2726
+ "/_matrix/client/v1/media/download/test/abcdefg123456",
2727
+ shorthand=False,
2728
+ access_token=self.tok,
2729
+ )
2730
+ self.assertEqual(channel9.code, 200)
2731
+
2732
+ channel10 = self.make_request(
2733
+ "GET",
2734
+ "/_matrix/media/r0/download/test/abcdefg123456",
2735
+ shorthand=False,
2736
+ access_token=self.tok,
2737
+ )
2738
+ self.assertEqual(channel10.code, 200)
2739
+
2740
+ def test_authenticated_media_etag(self) -> None:
2741
+ """Test that ETag works correctly with authenticated media over client
2742
+ APIs"""
2743
+
2744
+ # upload some local media with authentication on
2745
+ channel = self.make_request(
2746
+ "POST",
2747
+ "_matrix/media/v3/upload?filename=test_png_upload",
2748
+ SMALL_PNG,
2749
+ self.tok,
2750
+ shorthand=False,
2751
+ content_type=b"image/png",
2752
+ )
2753
+ self.assertEqual(channel.code, 200)
2754
+ res = channel.json_body.get("content_uri")
2755
+ assert res is not None
2756
+ uri = res.split("mxc://")[1]
2757
+
2758
+ # Check standard media endpoint
2759
+ self._check_caching(f"/download/{uri}")
2760
+
2761
+ # check thumbnails as well
2762
+ params = "?width=32&height=32&method=crop"
2763
+ self._check_caching(f"/thumbnail/{uri}{params}")
2764
+
2765
+ # Inject a piece of remote media.
2766
+ file_id = "abcdefg12345"
2767
+ file_info = FileInfo(server_name="lonelyIsland", file_id=file_id)
2768
+
2769
+ media_storage = self.hs.get_media_repository().media_storage
2770
+
2771
+ ctx = media_storage.store_into_file(file_info)
2772
+ (f, fname) = self.get_success(ctx.__aenter__())
2773
+ f.write(SMALL_PNG)
2774
+ self.get_success(ctx.__aexit__(None, None, None))
2775
+
2776
+ # we write the authenticated status when storing media, so this should pick up
2777
+ # config and authenticate the media
2778
+ self.get_success(
2779
+ self.store.store_cached_remote_media(
2780
+ origin="lonelyIsland",
2781
+ media_id="52",
2782
+ media_type="image/png",
2783
+ media_length=1,
2784
+ time_now_ms=self.clock.time_msec(),
2785
+ upload_name="remote_test.png",
2786
+ filesystem_id=file_id,
2787
+ sha256=file_id,
2788
+ )
2789
+ )
2790
+
2791
+ # ensure we have thumbnails for the non-dynamic code path
2792
+ if self.extra_config == {"dynamic_thumbnails": False}:
2793
+ self.get_success(
2794
+ self.repo._generate_thumbnails(
2795
+ "lonelyIsland", "52", file_id, "image/png"
2796
+ )
2797
+ )
2798
+
2799
+ self._check_caching("/download/lonelyIsland/52")
2800
+
2801
+ params = "?width=32&height=32&method=crop"
2802
+ self._check_caching(f"/thumbnail/lonelyIsland/52{params}")
2803
+
2804
+ def _check_caching(self, path: str) -> None:
2805
+ """
2806
+ Checks that:
2807
+ 1. fetching the path returns an ETag header
2808
+ 2. refetching with the ETag returns a 304 without a body
2809
+ 3. refetching with the ETag but through unauthenticated endpoint
2810
+ returns 404
2811
+ """
2812
+
2813
+ # Request media over authenticated endpoint, should be found
2814
+ channel1 = self.make_request(
2815
+ "GET",
2816
+ f"/_matrix/client/v1/media{path}",
2817
+ access_token=self.tok,
2818
+ shorthand=False,
2819
+ )
2820
+ self.assertEqual(channel1.code, 200)
2821
+
2822
+ # Should have a single ETag field
2823
+ etags = channel1.headers.getRawHeaders("ETag")
2824
+ self.assertIsNotNone(etags)
2825
+ assert etags is not None # For mypy
2826
+ self.assertEqual(len(etags), 1)
2827
+ etag = etags[0]
2828
+
2829
+ # Refetching with the etag should result in 304 and empty body.
2830
+ channel2 = self.make_request(
2831
+ "GET",
2832
+ f"/_matrix/client/v1/media{path}",
2833
+ access_token=self.tok,
2834
+ shorthand=False,
2835
+ custom_headers=[("If-None-Match", etag)],
2836
+ )
2837
+ self.assertEqual(channel2.code, 304)
2838
+ self.assertEqual(channel2.is_finished(), True)
2839
+ self.assertNotIn("body", channel2.result)
2840
+
2841
+ # Refetching with the etag but no access token should result in 404.
2842
+ channel3 = self.make_request(
2843
+ "GET",
2844
+ f"/_matrix/media/r0{path}",
2845
+ shorthand=False,
2846
+ custom_headers=[("If-None-Match", etag)],
2847
+ )
2848
+ self.assertEqual(channel3.code, 404)
2849
+
2850
+
2851
+ class MediaUploadLimits(unittest.HomeserverTestCase):
2852
+ """
2853
+ This test case simulates a homeserver with media upload limits configured.
2854
+ """
2855
+
2856
+ servlets = [
2857
+ media.register_servlets,
2858
+ login.register_servlets,
2859
+ admin.register_servlets,
2860
+ ]
2861
+
2862
+ def make_homeserver(self, reactor: MemoryReactor, clock: Clock) -> HomeServer:
2863
+ config = self.default_config()
2864
+
2865
+ self.storage_path = self.mktemp()
2866
+ self.media_store_path = self.mktemp()
2867
+ os.mkdir(self.storage_path)
2868
+ os.mkdir(self.media_store_path)
2869
+ config["media_store_path"] = self.media_store_path
2870
+
2871
+ provider_config = {
2872
+ "module": "synapse.media.storage_provider.FileStorageProviderBackend",
2873
+ "store_local": True,
2874
+ "store_synchronous": False,
2875
+ "store_remote": True,
2876
+ "config": {"directory": self.storage_path},
2877
+ }
2878
+
2879
+ config["media_storage_providers"] = [provider_config]
2880
+
2881
+ # These are the limits that we are testing
2882
+ config["media_upload_limits"] = [
2883
+ {"time_period": "1d", "max_size": "1K"},
2884
+ {"time_period": "1w", "max_size": "3K"},
2885
+ ]
2886
+
2887
+ return self.setup_test_homeserver(config=config)
2888
+
2889
+ def prepare(self, reactor: MemoryReactor, clock: Clock, hs: HomeServer) -> None:
2890
+ self.repo = hs.get_media_repository()
2891
+ self.client = hs.get_federation_http_client()
2892
+ self.store = hs.get_datastores().main
2893
+ self.user = self.register_user("user", "pass")
2894
+ self.tok = self.login("user", "pass")
2895
+
2896
+ def create_resource_dict(self) -> dict[str, Resource]:
2897
+ resources = super().create_resource_dict()
2898
+ resources["/_matrix/media"] = self.hs.get_media_repository_resource()
2899
+ return resources
2900
+
2901
+ def upload_media(self, size: int) -> FakeChannel:
2902
+ """Helper to upload media of a given size."""
2903
+ return self.make_request(
2904
+ "POST",
2905
+ "/_matrix/media/v3/upload",
2906
+ content=b"0" * size,
2907
+ access_token=self.tok,
2908
+ shorthand=False,
2909
+ content_type=b"text/plain",
2910
+ )
2911
+
2912
+ def test_upload_under_limit(self) -> None:
2913
+ """Test that uploading media under the limit works."""
2914
+ channel = self.upload_media(67)
2915
+ self.assertEqual(channel.code, 200)
2916
+
2917
+ def test_over_day_limit(self) -> None:
2918
+ """Test that uploading media over the daily limit fails."""
2919
+ channel = self.upload_media(500)
2920
+ self.assertEqual(channel.code, 200)
2921
+
2922
+ channel = self.upload_media(800)
2923
+ self.assertEqual(channel.code, 400)
2924
+
2925
+ def test_under_daily_limit(self) -> None:
2926
+ """Test that uploading media under the daily limit fails."""
2927
+ channel = self.upload_media(500)
2928
+ self.assertEqual(channel.code, 200)
2929
+
2930
+ self.reactor.advance(60 * 60 * 24) # Advance by one day
2931
+
2932
+ # This will succeed as the daily limit has reset
2933
+ channel = self.upload_media(800)
2934
+ self.assertEqual(channel.code, 200)
2935
+
2936
+ self.reactor.advance(60 * 60 * 24) # Advance by one day
2937
+
2938
+ # ... and again
2939
+ channel = self.upload_media(800)
2940
+ self.assertEqual(channel.code, 200)
2941
+
2942
+ def test_over_weekly_limit(self) -> None:
2943
+ """Test that uploading media over the weekly limit fails."""
2944
+ channel = self.upload_media(900)
2945
+ self.assertEqual(channel.code, 200)
2946
+
2947
+ self.reactor.advance(60 * 60 * 24) # Advance by one day
2948
+
2949
+ channel = self.upload_media(900)
2950
+ self.assertEqual(channel.code, 200)
2951
+
2952
+ self.reactor.advance(2 * 60 * 60 * 24) # Advance by one day
2953
+
2954
+ channel = self.upload_media(900)
2955
+ self.assertEqual(channel.code, 200)
2956
+
2957
+ self.reactor.advance(2 * 60 * 60 * 24) # Advance by one day
2958
+
2959
+ # This will fail as the weekly limit has been exceeded
2960
+ channel = self.upload_media(900)
2961
+ self.assertEqual(channel.code, 400)
2962
+
2963
+ # Reset the weekly limit by advancing a week
2964
+ self.reactor.advance(7 * 60 * 60 * 24) # Advance by 7 days
2965
+
2966
+ # This will succeed as the weekly limit has reset
2967
+ channel = self.upload_media(900)
2968
+ self.assertEqual(channel.code, 200)
2969
+
2970
+
2971
+ class MediaUploadLimitsModuleOverrides(unittest.HomeserverTestCase):
2972
+ """
2973
+ This test case simulates a homeserver with media upload limits being overridden by the module API.
2974
+ """
2975
+
2976
+ servlets = [
2977
+ media.register_servlets,
2978
+ login.register_servlets,
2979
+ admin.register_servlets,
2980
+ ]
2981
+
2982
+ def make_homeserver(self, reactor: MemoryReactor, clock: Clock) -> HomeServer:
2983
+ config = self.default_config()
2984
+
2985
+ self.storage_path = self.mktemp()
2986
+ self.media_store_path = self.mktemp()
2987
+ os.mkdir(self.storage_path)
2988
+ os.mkdir(self.media_store_path)
2989
+ config["media_store_path"] = self.media_store_path
2990
+
2991
+ provider_config = {
2992
+ "module": "synapse.media.storage_provider.FileStorageProviderBackend",
2993
+ "store_local": True,
2994
+ "store_synchronous": False,
2995
+ "store_remote": True,
2996
+ "config": {"directory": self.storage_path},
2997
+ }
2998
+
2999
+ config["media_storage_providers"] = [provider_config]
3000
+
3001
+ # default limits to use
3002
+ config["media_upload_limits"] = [
3003
+ {"time_period": "1d", "max_size": "1K"},
3004
+ {"time_period": "1w", "max_size": "3K"},
3005
+ ]
3006
+
3007
+ return self.setup_test_homeserver(config=config)
3008
+
3009
+ async def _get_media_upload_limits_for_user(
3010
+ self,
3011
+ user_id: str,
3012
+ ) -> list[MediaUploadLimit] | None:
3013
+ # user1 has custom limits
3014
+ if user_id == self.user1:
3015
+ # n.b. we return these in increasing duration order and Synapse will need to sort them correctly
3016
+ return [
3017
+ MediaUploadLimit(
3018
+ time_period_ms=Config.parse_duration("1d"), max_bytes=5000
3019
+ ),
3020
+ MediaUploadLimit(
3021
+ time_period_ms=Config.parse_duration("1w"), max_bytes=15000
3022
+ ),
3023
+ ]
3024
+ # user2 has no limits
3025
+ if user_id == self.user2:
3026
+ return []
3027
+ # otherwise use default
3028
+ return None
3029
+
3030
+ async def _on_media_upload_limit_exceeded(
3031
+ self,
3032
+ user_id: str,
3033
+ limit: MediaUploadLimit,
3034
+ sent_bytes: int,
3035
+ attempted_bytes: int,
3036
+ ) -> None:
3037
+ self.last_media_upload_limit_exceeded: dict[str, object] | None = {
3038
+ "user_id": user_id,
3039
+ "limit": limit,
3040
+ "sent_bytes": sent_bytes,
3041
+ "attempted_bytes": attempted_bytes,
3042
+ }
3043
+
3044
+ def prepare(self, reactor: MemoryReactor, clock: Clock, hs: HomeServer) -> None:
3045
+ self.repo = hs.get_media_repository()
3046
+ self.client = hs.get_federation_http_client()
3047
+ self.store = hs.get_datastores().main
3048
+ self.user1 = self.register_user("user1", "pass")
3049
+ self.tok1 = self.login("user1", "pass")
3050
+ self.user2 = self.register_user("user2", "pass")
3051
+ self.tok2 = self.login("user2", "pass")
3052
+ self.user3 = self.register_user("user3", "pass")
3053
+ self.tok3 = self.login("user3", "pass")
3054
+ self.last_media_upload_limit_exceeded = None
3055
+ self.hs.get_module_api().register_media_repository_callbacks(
3056
+ get_media_upload_limits_for_user=self._get_media_upload_limits_for_user,
3057
+ on_media_upload_limit_exceeded=self._on_media_upload_limit_exceeded,
3058
+ )
3059
+
3060
+ def create_resource_dict(self) -> dict[str, Resource]:
3061
+ resources = super().create_resource_dict()
3062
+ resources["/_matrix/media"] = self.hs.get_media_repository_resource()
3063
+ return resources
3064
+
3065
+ def upload_media(self, size: int, tok: str) -> FakeChannel:
3066
+ """Helper to upload media of a given size with a given token."""
3067
+ return self.make_request(
3068
+ "POST",
3069
+ "/_matrix/media/v3/upload",
3070
+ content=b"0" * size,
3071
+ access_token=tok,
3072
+ shorthand=False,
3073
+ content_type=b"text/plain",
3074
+ )
3075
+
3076
+ def test_upload_under_limit(self) -> None:
3077
+ """Test that uploading media under the limit works."""
3078
+
3079
+ # User 1 uploads 100 bytes
3080
+ channel = self.upload_media(100, self.tok1)
3081
+ self.assertEqual(channel.code, 200)
3082
+
3083
+ # User 2 (unlimited) uploads 100 bytes
3084
+ channel = self.upload_media(100, self.tok2)
3085
+ self.assertEqual(channel.code, 200)
3086
+
3087
+ # User 3 (default) uploads 100 bytes
3088
+ channel = self.upload_media(100, self.tok3)
3089
+ self.assertEqual(channel.code, 200)
3090
+
3091
+ self.assertEqual(self.last_media_upload_limit_exceeded, None)
3092
+
3093
+ def test_uses_custom_limit(self) -> None:
3094
+ """Test that uploading media over the module provided daily limit fails."""
3095
+
3096
+ # User 1 uploads 3000 bytes
3097
+ channel = self.upload_media(3000, self.tok1)
3098
+ self.assertEqual(channel.code, 200)
3099
+
3100
+ # User 1 attempts to upload 4000 bytes taking it over the limit
3101
+ channel = self.upload_media(4000, self.tok1)
3102
+ self.assertEqual(channel.code, 400)
3103
+ assert self.last_media_upload_limit_exceeded is not None
3104
+ self.assertEqual(self.last_media_upload_limit_exceeded["user_id"], self.user1)
3105
+ self.assertEqual(
3106
+ self.last_media_upload_limit_exceeded["limit"],
3107
+ MediaUploadLimit(
3108
+ max_bytes=5000, time_period_ms=Config.parse_duration("1d")
3109
+ ),
3110
+ )
3111
+ self.assertEqual(self.last_media_upload_limit_exceeded["sent_bytes"], 3000)
3112
+ self.assertEqual(self.last_media_upload_limit_exceeded["attempted_bytes"], 4000)
3113
+
3114
+ # User 1 attempts to upload 20000 bytes which is over the weekly limit
3115
+ # This tests that the limits have been sorted as expected
3116
+ channel = self.upload_media(20000, self.tok1)
3117
+ self.assertEqual(channel.code, 400)
3118
+ assert self.last_media_upload_limit_exceeded is not None
3119
+ self.assertEqual(self.last_media_upload_limit_exceeded["user_id"], self.user1)
3120
+ self.assertEqual(
3121
+ self.last_media_upload_limit_exceeded["limit"],
3122
+ MediaUploadLimit(
3123
+ max_bytes=15000, time_period_ms=Config.parse_duration("1w")
3124
+ ),
3125
+ )
3126
+ self.assertEqual(self.last_media_upload_limit_exceeded["sent_bytes"], 3000)
3127
+ self.assertEqual(
3128
+ self.last_media_upload_limit_exceeded["attempted_bytes"], 20000
3129
+ )
3130
+
3131
+ def test_uses_unlimited(self) -> None:
3132
+ """Test that unlimited user is not limited when module returns []."""
3133
+ # User 2 uploads 10000 bytes which is over the default limit
3134
+ channel = self.upload_media(10000, self.tok2)
3135
+ self.assertEqual(channel.code, 200)
3136
+ self.assertEqual(self.last_media_upload_limit_exceeded, None)
3137
+
3138
+ def test_uses_defaults(self) -> None:
3139
+ """Test that the default limits are applied when module returned None."""
3140
+ # User 3 uploads 500 bytes
3141
+ channel = self.upload_media(500, self.tok3)
3142
+ self.assertEqual(channel.code, 200)
3143
+
3144
+ # User 3 uploads 800 bytes which is over the limit
3145
+ channel = self.upload_media(800, self.tok3)
3146
+ self.assertEqual(channel.code, 400)
3147
+ assert self.last_media_upload_limit_exceeded is not None
3148
+ self.assertEqual(self.last_media_upload_limit_exceeded["user_id"], self.user3)
3149
+ self.assertEqual(
3150
+ self.last_media_upload_limit_exceeded["limit"],
3151
+ MediaUploadLimit(
3152
+ max_bytes=1024, time_period_ms=Config.parse_duration("1d")
3153
+ ),
3154
+ )
3155
+ self.assertEqual(self.last_media_upload_limit_exceeded["sent_bytes"], 500)
3156
+ self.assertEqual(self.last_media_upload_limit_exceeded["attempted_bytes"], 800)