From fcbc274d01ad2152d7d2c56f80bcb86ec40eed07 Mon Sep 17 00:00:00 2001 From: upstream-mirror Date: Tue, 15 Sep 2026 16:13:53 +0500 Subject: [PATCH] snapshot of https://github.com/jitsi/jicofo.git @ 9ab59aa3d348e282da89da2c9f11ecbecd3f7b79 (2026-09-15) --- .editorconfig | 9 + .gitattributes | 1 + .github/ISSUE_TEMPLATE/bug-report.md | 27 + .github/workflows/maven.yml | 43 + .gitignore | 15 + CLAUDE.md | 159 + LICENSE | 202 ++ README.md | 104 + SECURITY.md | 9 + checkstyle.xml | 45 + debian/README.Debian | 5 + debian/changelog | 6 + debian/compat | 1 + debian/control | 14 + debian/copyright | 13 + debian/docs | 0 debian/init.d | 101 + debian/install | 6 + debian/manpage.1 | 38 + debian/po/POTFILES.in | 1 + debian/po/templates.pot | 54 + debian/postinst | 167 + debian/postrm | 57 + debian/preinst | 14 + debian/rules | 30 + debian/source/format | 2 + debian/templates | 9 + doc/conference-request.md | 61 + doc/diagram.key | Bin 0 -> 1647051 bytes doc/diagram.png | Bin 0 -> 192474 bytes doc/health-checks.md | 29 + doc/reservation.md | 1 + doc/shibboleth.md | 5 + jicofo-common/pom.xml | 412 +++ jicofo-common/spotbugs-exclude.xml | 69 + .../protocol/xmpp/log/ExcludeXmppPackets.java | 33 + .../protocol/xmpp/log/IncludeXmppPackets.java | 33 + .../protocol/xmpp/log/PacketDebugger.java | 104 + .../xmpp/log/XmppPacketsFileHandler.java | 56 + .../org/jitsi/jicofo/xmpp/BaseBrewery.java | 425 +++ .../kotlin/org/jitsi/jicofo/JicofoConfig.kt | 52 + .../main/kotlin/org/jitsi/jicofo/MediaType.kt | 24 + .../kotlin/org/jitsi/jicofo/OctoConfig.kt | 40 + .../main/kotlin/org/jitsi/jicofo/TaskPools.kt | 57 + .../org/jitsi/jicofo/codec/CodecConfig.kt | 183 + .../org/jitsi/jicofo/codec/CodecUtil.kt | 326 ++ .../org/jitsi/jicofo/codec/OfferOptions.kt | 31 + .../conference/source/ConferenceSourceMap.kt | 222 ++ .../conference/source/EndpointSourceSet.kt | 283 ++ .../jitsi/jicofo/conference/source/Source.kt | 120 + .../jicofo/conference/source/SsrcGroup.kt | 89 + .../conference/source/SsrcGroupSemantics.kt | 36 + .../source/ValidatingConferenceSourceMap.kt | 358 ++ .../jicofo/conference/source/VideoType.kt | 27 + .../org/jitsi/jicofo/metrics/GlobalMetrics.kt | 40 + .../jitsi/jicofo/metrics/IceRestartMetrics.kt | 67 + .../jicofo/metrics/JicofoMetricsContainer.kt | 38 + .../org/jitsi/jicofo/util/PendingCount.kt | 53 + .../jitsi/jicofo/util/SynchronizedDelegate.kt | 33 + .../org/jitsi/jicofo/util/TracingUtil.kt | 25 + .../org/jitsi/jicofo/util/WeakValueMap.kt | 51 + .../jitsi/jicofo/xmpp/AbstractIqHandler.kt | 137 + .../kotlin/org/jitsi/jicofo/xmpp/Features.kt | 94 + .../org/jitsi/jicofo/xmpp/JingleUtils.kt | 43 + .../org/jitsi/jicofo/xmpp/JsonMessage.kt | 132 + .../main/kotlin/org/jitsi/jicofo/xmpp/Util.kt | 76 + .../org/jitsi/jicofo/xmpp/XmppCapsStats.kt | 69 + .../org/jitsi/jicofo/xmpp/XmppConfig.kt | 298 ++ .../jitsi/jicofo/xmpp/XmppConnectionEnum.kt | 18 + .../org/jitsi/jicofo/xmpp/XmppProvider.kt | 390 ++ .../xmpp/jingle/JingleIqRequestHandler.kt | 69 + .../xmpp/jingle/JingleRequestHandler.kt | 90 + .../jitsi/jicofo/xmpp/jingle/JingleSession.kt | 472 +++ .../jitsi/jicofo/xmpp/jingle/JingleStats.kt | 68 + .../org/jitsi/jicofo/xmpp/muc/ChatRoom.kt | 213 ++ .../org/jitsi/jicofo/xmpp/muc/ChatRoomImpl.kt | 835 +++++ .../jitsi/jicofo/xmpp/muc/ChatRoomListener.kt | 50 + .../jitsi/jicofo/xmpp/muc/ChatRoomMember.kt | 96 + .../jicofo/xmpp/muc/ChatRoomMemberImpl.kt | 325 ++ .../org/jitsi/jicofo/xmpp/muc/MemberRole.kt | 55 + .../org/jitsi/jicofo/xmpp/muc/SourceInfo.kt | 80 + .../src/main/resources/reference.conf | 214 ++ .../protocol/xmpp/log/PacketDebuggerTest.kt | 42 + .../jicofo/JicofoCommonKotestProjectConfig.kt | 33 + .../org/jitsi/jicofo/JicofoConfigTest.kt | 60 + .../kotlin/org/jitsi/jicofo/OctoConfigTest.kt | 43 + .../org/jitsi/jicofo/codec/CodecConfigTest.kt | 288 ++ .../org/jitsi/jicofo/codec/CodecUtilTest.kt | 175 + .../source/ConferenceSourceMapTest.kt | 261 ++ .../source/EndpointSourceSetTest.kt | 164 + .../jicofo/conference/source/SourceTest.kt | 79 + .../jicofo/conference/source/SsrcGroupTest.kt | 77 + .../source/SsrcValidationPerfTest.kt | 129 + .../ValidatingConferenceSourceMapTest.kt | 417 +++ .../org/jitsi/jicofo/mock/InPlaceExecutors.kt | 44 + .../org/jitsi/jicofo/mock/MockChatRoom.kt | 103 + .../jitsi/jicofo/mock/MockXmppConnection.kt | 54 + .../org/jitsi/jicofo/mock/MockXmppProvider.kt | 70 + .../org/jitsi/jicofo/mock/PendingExecutor.kt | 47 + .../org/jitsi/jicofo/util/ConfigHelpers.kt | 25 + .../org/jitsi/jicofo/util/JsonAssertions.kt | 27 + .../org/jitsi/jicofo/util/PendingCountTest.kt | 100 + .../org/jitsi/jicofo/util/WeakValueMapTest.kt | 86 + .../jicofo/xmpp/AbstractIqHandlerTest.kt | 117 + .../org/jitsi/jicofo/xmpp/FeaturesTest.kt | 30 + .../org/jitsi/jicofo/xmpp/JsonMessageTest.kt | 326 ++ .../org/jitsi/jicofo/xmpp/XmppConfigTest.kt | 189 + .../jitsi/jicofo/xmpp/muc/ChatRoomImplTest.kt | 233 ++ .../jicofo/xmpp/muc/ChatRoomMemberImplTest.kt | 187 + .../jitsi/jicofo/xmpp/muc/MemberRoleTest.kt | 77 + .../jitsi/jicofo/xmpp/muc/SourceInfoTest.kt | 89 + jicofo-selector/pom.xml | 415 +++ jicofo-selector/spotbugs-exclude.xml | 63 + .../org/jitsi/jicofo/bridge/JvbDoctor.java | 398 +++ .../org/jitsi/jicofo/TranscriptionConfig.kt | 106 + .../org/jitsi/jicofo/TranslationConfig.kt | 127 + .../kotlin/org/jitsi/jicofo/bridge/Bridge.kt | 384 ++ .../org/jitsi/jicofo/bridge/BridgeConfig.kt | 231 ++ .../org/jitsi/jicofo/bridge/BridgeMetrics.kt | 33 + .../jitsi/jicofo/bridge/BridgeMucDetector.kt | 54 + .../jicofo/bridge/BridgeSelectionStrategy.kt | 364 ++ .../org/jitsi/jicofo/bridge/BridgeSelector.kt | 302 ++ .../kotlin/org/jitsi/jicofo/bridge/Cascade.kt | 326 ++ .../jicofo/bridge/HealthCheckListener.kt | 26 + .../IntraRegionBridgeSelectionStrategy.kt | 47 + .../RegionBasedBridgeSelectionStrategy.kt | 123 + .../bridge/SingleBridgeSelectionStrategy.kt | 57 + .../bridge/SingleMeshTopologyStrategy.kt | 36 + .../bridge/SplitBridgeSelectionStrategy.kt | 63 + .../bridge/TopologySelectionStrategy.kt | 36 + .../kotlin/org/jitsi/jicofo/bridge/Util.kt | 24 + .../jicofo/bridge/VisitorSelectionStrategy.kt | 54 + .../jicofo/bridge/VisitorTopologyStrategy.kt | 99 + .../jicofo/bridge/colibri/Colibri2Session.kt | 702 ++++ .../bridge/colibri/ColibriAllocation.kt | 35 + .../bridge/colibri/ColibriSessionManager.kt | 171 + .../bridge/colibri/ColibriV2SessionManager.kt | 1087 ++++++ .../jicofo/bridge/colibri/ConnectSpec.kt | 69 + .../jitsi/jicofo/bridge/colibri/Exceptions.kt | 29 + .../jitsi/jicofo/bridge/colibri/Extensions.kt | 149 + .../jicofo/bridge/colibri/ParticipantInfo.kt | 78 + .../src/main/resources/reference.conf | 181 + .../JicofoSelectorKotestProjectConfig.kt | 33 + .../jitsi/jicofo/TranscriptionConfigTest.kt | 257 ++ .../org/jitsi/jicofo/TranslationConfigTest.kt | 93 + .../jitsi/jicofo/bridge/BridgeConfigTest.kt | 128 + .../jitsi/jicofo/bridge/BridgeReleaseTest.kt | 218 ++ .../bridge/BridgeSelectionStrategyTest.kt | 176 + .../jitsi/jicofo/bridge/BridgeSelectorTest.kt | 289 ++ .../org/jitsi/jicofo/bridge/BridgeTest.kt | 147 + .../org/jitsi/jicofo/bridge/CascadeTest.kt | 414 +++ .../bridge/MaxParticipantsPerIntervalTest.kt | 148 + .../jicofo/bridge/RegionBasedSelectionTest.kt | 216 ++ .../org/jitsi/jicofo/bridge/UtilTest.kt | 41 + .../colibri/ColibriTextTranslationTest.kt | 217 ++ .../colibri/ColibriTranscriptionTest.kt | 243 ++ .../bridge/colibri/ColibriTranslationTest.kt | 217 ++ .../colibri/ColibriV2SessionManagerTest.kt | 462 +++ .../jicofo/bridge/colibri/ConnectSpecTest.kt | 87 + .../colibri/PerSourceTranslatorSpecsTest.kt | 101 + .../jitsi/jicofo/mock/TestColibri2Server.kt | 321 ++ jicofo/pom.xml | 500 +++ jicofo/spotbugs-exclude.xml | 66 + jicofo/src/assembly/archive.xml | 55 + .../src/main/java/org/jitsi/jicofo/Main.java | 147 + .../jicofo/auth/AbstractAuthAuthority.java | 531 +++ .../jicofo/auth/AuthenticationAuthority.java | 119 + .../jicofo/auth/AuthenticationListener.java | 40 + .../jicofo/auth/AuthenticationSession.java | 173 + .../org/jitsi/jicofo/auth/ErrorFactory.java | 88 + .../jicofo/auth/ExternalJWTAuthority.java | 72 + .../jicofo/auth/XMPPDomainAuthAuthority.java | 127 + .../jicofo/codec/JingleOfferFactory.java | 187 + .../conference/JitsiMeetConference.java | 189 + .../conference/JitsiMeetConferenceImpl.java | 3142 +++++++++++++++++ .../jitsi/jicofo/conference/MuteResult.java | 26 + .../conference/ParticipantInviteRunnable.java | 496 +++ .../org/jitsi/jicofo/jibri/JibriSession.java | 966 +++++ .../org/jitsi/jicofo/util/ErrorResponse.java | 41 + .../jicofo/version/CurrentVersionImpl.java | 101 + .../jitsi/jicofo/ClientRequirementsConfig.kt | 152 + .../org/jitsi/jicofo/ConferenceConfig.kt | 147 + .../org/jitsi/jicofo/ConferenceRequest.kt | 94 + .../org/jitsi/jicofo/ConferenceStore.kt | 47 + .../kotlin/org/jitsi/jicofo/FocusManager.kt | 399 +++ .../kotlin/org/jitsi/jicofo/JicofoServices.kt | 267 ++ .../src/main/kotlin/org/jitsi/jicofo/Offer.kt | 27 + .../kotlin/org/jitsi/jicofo/ReinviteMethod.kt | 20 + .../org/jitsi/jicofo/auth/AuthConfig.kt | 92 + .../jicofo/bridgeload/LoadRedistributor.kt | 273 ++ .../conference/ClientRequirementsHandler.kt | 226 ++ .../jicofo/conference/ConferenceMetrics.kt | 149 + .../jitsi/jicofo/conference/ConferenceUtil.kt | 97 + .../jicofo/conference/JitsiMeetConfig.kt | 64 + .../jitsi/jicofo/conference/Participant.kt | 589 +++ .../jicofo/conference/SourceSignaling.kt | 103 + .../jicofo/conference/SourcesToAddOrRemove.kt | 40 + .../ConferenceTranslationManager.kt | 133 + .../translation/TranslationSourceManager.kt | 132 + .../org/jitsi/jicofo/health/HealthConfig.kt | 51 + .../jicofo/health/JicofoHealthChecker.kt | 187 + .../org/jitsi/jicofo/jibri/BaseJibri.kt | 215 ++ .../org/jitsi/jicofo/jibri/JibriConfig.kt | 70 + .../org/jitsi/jicofo/jibri/JibriDetector.kt | 194 + .../jicofo/jibri/JibriDetectorMetrics.kt | 79 + .../org/jitsi/jicofo/jibri/JibriRecorder.kt | 193 + .../org/jitsi/jicofo/jibri/JibriSipGateway.kt | 149 + .../org/jitsi/jicofo/jibri/JibriStats.kt | 78 + .../org/jitsi/jicofo/jigasi/JigasiConfig.kt | 52 + .../org/jitsi/jicofo/jigasi/JigasiDetector.kt | 167 + .../org/jitsi/jicofo/ktor/Application.kt | 354 ++ .../org/jitsi/jicofo/ktor/RestConfig.kt | 62 + .../jitsi/jicofo/ktor/exception/Exceptions.kt | 48 + .../org/jitsi/jicofo/util/Cancelable.kt | 22 + .../org/jitsi/jicofo/util/OfferOptionsUtil.kt | 31 + .../jitsi/jicofo/util/PreferenceAggregator.kt | 151 + .../org/jitsi/jicofo/util/RateLimitedStat.kt | 107 + .../jitsi/jicofo/visitors/VisitorsConfig.kt | 55 + .../jicofo/xmpp/AuthenticationIqHandler.kt | 101 + .../jitsi/jicofo/xmpp/AvModerationHandler.kt | 130 + .../jitsi/jicofo/xmpp/ConferenceIqHandler.kt | 308 ++ .../org/jitsi/jicofo/xmpp/JibriIqHandler.kt | 74 + .../org/jitsi/jicofo/xmpp/JigasiIqHandler.kt | 272 ++ .../org/jitsi/jicofo/xmpp/MuteIqHandler.kt | 184 + .../jitsi/jicofo/xmpp/RoomMetadataHandler.kt | 101 + .../kotlin/org/jitsi/jicofo/xmpp/Smack.kt | 169 + .../jicofo/xmpp/VisitorConnectionMonitor.kt | 68 + .../org/jitsi/jicofo/xmpp/VisitorsManager.kt | 119 + .../org/jitsi/jicofo/xmpp/XmppServices.kt | 158 + .../jicofo/xmpp/muc/ChatRoomRoleManager.kt | 171 + jicofo/src/main/resources/reference.conf | 254 ++ .../org/jitsi/jicofo/ConferenceConfigTest.kt | 58 + .../jitsi/jicofo/ConferenceIqHandlerTest.kt | 58 + .../org/jitsi/jicofo/ConferenceRequestTest.kt | 89 + .../kotlin/org/jitsi/jicofo/DebugStateTest.kt | 102 + .../org/jitsi/jicofo/KotestProjectConfig.kt | 29 + .../org/jitsi/jicofo/auth/AuthConfigTest.kt | 86 + .../auth/XMPPDomainAuthAuthorityTest.kt | 217 ++ .../org/jitsi/jicofo/bridge/BridgePinTest.kt | 114 + .../bridgeload/LoadRedistributorTest.kt | 140 + .../jicofo/codec/JingleOfferFactoryTest.kt | 62 + .../ClientRequirementsHandlerTest.kt | 252 ++ .../jitsi/jicofo/conference/ConferenceTest.kt | 319 ++ .../jicofo/conference/ConferenceUtilTest.kt | 95 + .../conference/ConferenceVisitorsTest.kt | 154 + .../jitsi/jicofo/conference/IceRestartTest.kt | 194 + .../ParticipantInviteRunnableTest.kt | 179 + .../conference/source/SourceSignalingTest.kt | 164 + .../TranslationSourceManagerTest.kt | 140 + .../jitsi/jicofo/jibri/JibriDetectorTest.kt | 181 + .../jitsi/jicofo/jibri/JibriSessionTest.kt | 147 + .../jitsi/jicofo/jigasi/JigasiSelectorTest.kt | 143 + .../mock/ColibriAndJingleXmppConnection.kt | 138 + .../jitsi/jicofo/mock/ConferenceHarness.kt | 136 + .../jitsi/jicofo/util/ListConferenceStore.kt | 32 + .../jicofo/util/PreferenceAggregatorTest.kt | 109 + .../jitsi/jicofo/util/RateLimitedStatTest.kt | 102 + .../jicofo/xmpp/AvModerationHandlerTest.kt | 114 + .../jitsi/jicofo/xmpp/MuteIqHandlerTest.kt | 145 + .../jicofo/xmpp/RoomMetadataHandlerTest.kt | 95 + .../xmpp/VisitorConnectionMonitorTest.kt | 82 + .../jicofo/xmpp/jigasi/JigasiIqHandlerTest.kt | 194 + .../xmpp/muc/ChatRoomMemberDiarizeTest.kt | 63 + .../ChatRoomMemberTranslationLanguageTest.kt | 125 + .../xmpp/muc/ChatRoomRoleManagerTest.kt | 145 + .../muc/ChatRoomTranslationLanguagesTest.kt | 65 + lib/logging.properties | 28 + pom.xml | 300 ++ resources/build_deb_package.sh | 71 + resources/collect-dump-logs.sh | 39 + resources/config/jicofo-logrotate.d | 10 + resources/jicofo.bat | 22 + resources/jicofo.sh | 36 + resources/mvn-exec.sh | 11 + script/graceful_shutdown.py | 166 + 275 files changed, 41230 insertions(+) create mode 100644 .editorconfig create mode 100644 .gitattributes create mode 100644 .github/ISSUE_TEMPLATE/bug-report.md create mode 100644 .github/workflows/maven.yml create mode 100644 .gitignore create mode 100644 CLAUDE.md create mode 100644 LICENSE create mode 100644 README.md create mode 100644 SECURITY.md create mode 100644 checkstyle.xml create mode 100644 debian/README.Debian create mode 100644 debian/changelog create mode 100644 debian/compat create mode 100644 debian/control create mode 100644 debian/copyright create mode 100644 debian/docs create mode 100644 debian/init.d create mode 100644 debian/install create mode 100644 debian/manpage.1 create mode 100644 debian/po/POTFILES.in create mode 100644 debian/po/templates.pot create mode 100644 debian/postinst create mode 100644 debian/postrm create mode 100644 debian/preinst create mode 100755 debian/rules create mode 100644 debian/source/format create mode 100644 debian/templates create mode 100644 doc/conference-request.md create mode 100755 doc/diagram.key create mode 100644 doc/diagram.png create mode 100644 doc/health-checks.md create mode 100644 doc/reservation.md create mode 100644 doc/shibboleth.md create mode 100644 jicofo-common/pom.xml create mode 100644 jicofo-common/spotbugs-exclude.xml create mode 100644 jicofo-common/src/main/java/org/jitsi/impl/protocol/xmpp/log/ExcludeXmppPackets.java create mode 100644 jicofo-common/src/main/java/org/jitsi/impl/protocol/xmpp/log/IncludeXmppPackets.java create mode 100644 jicofo-common/src/main/java/org/jitsi/impl/protocol/xmpp/log/PacketDebugger.java create mode 100644 jicofo-common/src/main/java/org/jitsi/impl/protocol/xmpp/log/XmppPacketsFileHandler.java create mode 100644 jicofo-common/src/main/java/org/jitsi/jicofo/xmpp/BaseBrewery.java create mode 100644 jicofo-common/src/main/kotlin/org/jitsi/jicofo/JicofoConfig.kt create mode 100644 jicofo-common/src/main/kotlin/org/jitsi/jicofo/MediaType.kt create mode 100644 jicofo-common/src/main/kotlin/org/jitsi/jicofo/OctoConfig.kt create mode 100644 jicofo-common/src/main/kotlin/org/jitsi/jicofo/TaskPools.kt create mode 100644 jicofo-common/src/main/kotlin/org/jitsi/jicofo/codec/CodecConfig.kt create mode 100644 jicofo-common/src/main/kotlin/org/jitsi/jicofo/codec/CodecUtil.kt create mode 100644 jicofo-common/src/main/kotlin/org/jitsi/jicofo/codec/OfferOptions.kt create mode 100644 jicofo-common/src/main/kotlin/org/jitsi/jicofo/conference/source/ConferenceSourceMap.kt create mode 100644 jicofo-common/src/main/kotlin/org/jitsi/jicofo/conference/source/EndpointSourceSet.kt create mode 100644 jicofo-common/src/main/kotlin/org/jitsi/jicofo/conference/source/Source.kt create mode 100644 jicofo-common/src/main/kotlin/org/jitsi/jicofo/conference/source/SsrcGroup.kt create mode 100644 jicofo-common/src/main/kotlin/org/jitsi/jicofo/conference/source/SsrcGroupSemantics.kt create mode 100644 jicofo-common/src/main/kotlin/org/jitsi/jicofo/conference/source/ValidatingConferenceSourceMap.kt create mode 100644 jicofo-common/src/main/kotlin/org/jitsi/jicofo/conference/source/VideoType.kt create mode 100644 jicofo-common/src/main/kotlin/org/jitsi/jicofo/metrics/GlobalMetrics.kt create mode 100644 jicofo-common/src/main/kotlin/org/jitsi/jicofo/metrics/IceRestartMetrics.kt create mode 100644 jicofo-common/src/main/kotlin/org/jitsi/jicofo/metrics/JicofoMetricsContainer.kt create mode 100644 jicofo-common/src/main/kotlin/org/jitsi/jicofo/util/PendingCount.kt create mode 100644 jicofo-common/src/main/kotlin/org/jitsi/jicofo/util/SynchronizedDelegate.kt create mode 100644 jicofo-common/src/main/kotlin/org/jitsi/jicofo/util/TracingUtil.kt create mode 100644 jicofo-common/src/main/kotlin/org/jitsi/jicofo/util/WeakValueMap.kt create mode 100644 jicofo-common/src/main/kotlin/org/jitsi/jicofo/xmpp/AbstractIqHandler.kt create mode 100644 jicofo-common/src/main/kotlin/org/jitsi/jicofo/xmpp/Features.kt create mode 100644 jicofo-common/src/main/kotlin/org/jitsi/jicofo/xmpp/JingleUtils.kt create mode 100644 jicofo-common/src/main/kotlin/org/jitsi/jicofo/xmpp/JsonMessage.kt create mode 100644 jicofo-common/src/main/kotlin/org/jitsi/jicofo/xmpp/Util.kt create mode 100644 jicofo-common/src/main/kotlin/org/jitsi/jicofo/xmpp/XmppCapsStats.kt create mode 100644 jicofo-common/src/main/kotlin/org/jitsi/jicofo/xmpp/XmppConfig.kt create mode 100644 jicofo-common/src/main/kotlin/org/jitsi/jicofo/xmpp/XmppConnectionEnum.kt create mode 100644 jicofo-common/src/main/kotlin/org/jitsi/jicofo/xmpp/XmppProvider.kt create mode 100644 jicofo-common/src/main/kotlin/org/jitsi/jicofo/xmpp/jingle/JingleIqRequestHandler.kt create mode 100644 jicofo-common/src/main/kotlin/org/jitsi/jicofo/xmpp/jingle/JingleRequestHandler.kt create mode 100644 jicofo-common/src/main/kotlin/org/jitsi/jicofo/xmpp/jingle/JingleSession.kt create mode 100644 jicofo-common/src/main/kotlin/org/jitsi/jicofo/xmpp/jingle/JingleStats.kt create mode 100644 jicofo-common/src/main/kotlin/org/jitsi/jicofo/xmpp/muc/ChatRoom.kt create mode 100644 jicofo-common/src/main/kotlin/org/jitsi/jicofo/xmpp/muc/ChatRoomImpl.kt create mode 100644 jicofo-common/src/main/kotlin/org/jitsi/jicofo/xmpp/muc/ChatRoomListener.kt create mode 100644 jicofo-common/src/main/kotlin/org/jitsi/jicofo/xmpp/muc/ChatRoomMember.kt create mode 100644 jicofo-common/src/main/kotlin/org/jitsi/jicofo/xmpp/muc/ChatRoomMemberImpl.kt create mode 100644 jicofo-common/src/main/kotlin/org/jitsi/jicofo/xmpp/muc/MemberRole.kt create mode 100644 jicofo-common/src/main/kotlin/org/jitsi/jicofo/xmpp/muc/SourceInfo.kt create mode 100644 jicofo-common/src/main/resources/reference.conf create mode 100644 jicofo-common/src/test/kotlin/org/jitsi/impl/protocol/xmpp/log/PacketDebuggerTest.kt create mode 100644 jicofo-common/src/test/kotlin/org/jitsi/jicofo/JicofoCommonKotestProjectConfig.kt create mode 100644 jicofo-common/src/test/kotlin/org/jitsi/jicofo/JicofoConfigTest.kt create mode 100644 jicofo-common/src/test/kotlin/org/jitsi/jicofo/OctoConfigTest.kt create mode 100644 jicofo-common/src/test/kotlin/org/jitsi/jicofo/codec/CodecConfigTest.kt create mode 100644 jicofo-common/src/test/kotlin/org/jitsi/jicofo/codec/CodecUtilTest.kt create mode 100644 jicofo-common/src/test/kotlin/org/jitsi/jicofo/conference/source/ConferenceSourceMapTest.kt create mode 100644 jicofo-common/src/test/kotlin/org/jitsi/jicofo/conference/source/EndpointSourceSetTest.kt create mode 100644 jicofo-common/src/test/kotlin/org/jitsi/jicofo/conference/source/SourceTest.kt create mode 100644 jicofo-common/src/test/kotlin/org/jitsi/jicofo/conference/source/SsrcGroupTest.kt create mode 100644 jicofo-common/src/test/kotlin/org/jitsi/jicofo/conference/source/SsrcValidationPerfTest.kt create mode 100644 jicofo-common/src/test/kotlin/org/jitsi/jicofo/conference/source/ValidatingConferenceSourceMapTest.kt create mode 100644 jicofo-common/src/test/kotlin/org/jitsi/jicofo/mock/InPlaceExecutors.kt create mode 100644 jicofo-common/src/test/kotlin/org/jitsi/jicofo/mock/MockChatRoom.kt create mode 100644 jicofo-common/src/test/kotlin/org/jitsi/jicofo/mock/MockXmppConnection.kt create mode 100644 jicofo-common/src/test/kotlin/org/jitsi/jicofo/mock/MockXmppProvider.kt create mode 100644 jicofo-common/src/test/kotlin/org/jitsi/jicofo/mock/PendingExecutor.kt create mode 100644 jicofo-common/src/test/kotlin/org/jitsi/jicofo/util/ConfigHelpers.kt create mode 100644 jicofo-common/src/test/kotlin/org/jitsi/jicofo/util/JsonAssertions.kt create mode 100644 jicofo-common/src/test/kotlin/org/jitsi/jicofo/util/PendingCountTest.kt create mode 100644 jicofo-common/src/test/kotlin/org/jitsi/jicofo/util/WeakValueMapTest.kt create mode 100644 jicofo-common/src/test/kotlin/org/jitsi/jicofo/xmpp/AbstractIqHandlerTest.kt create mode 100644 jicofo-common/src/test/kotlin/org/jitsi/jicofo/xmpp/FeaturesTest.kt create mode 100644 jicofo-common/src/test/kotlin/org/jitsi/jicofo/xmpp/JsonMessageTest.kt create mode 100644 jicofo-common/src/test/kotlin/org/jitsi/jicofo/xmpp/XmppConfigTest.kt create mode 100644 jicofo-common/src/test/kotlin/org/jitsi/jicofo/xmpp/muc/ChatRoomImplTest.kt create mode 100644 jicofo-common/src/test/kotlin/org/jitsi/jicofo/xmpp/muc/ChatRoomMemberImplTest.kt create mode 100644 jicofo-common/src/test/kotlin/org/jitsi/jicofo/xmpp/muc/MemberRoleTest.kt create mode 100644 jicofo-common/src/test/kotlin/org/jitsi/jicofo/xmpp/muc/SourceInfoTest.kt create mode 100644 jicofo-selector/pom.xml create mode 100644 jicofo-selector/spotbugs-exclude.xml create mode 100644 jicofo-selector/src/main/java/org/jitsi/jicofo/bridge/JvbDoctor.java create mode 100644 jicofo-selector/src/main/kotlin/org/jitsi/jicofo/TranscriptionConfig.kt create mode 100644 jicofo-selector/src/main/kotlin/org/jitsi/jicofo/TranslationConfig.kt create mode 100644 jicofo-selector/src/main/kotlin/org/jitsi/jicofo/bridge/Bridge.kt create mode 100644 jicofo-selector/src/main/kotlin/org/jitsi/jicofo/bridge/BridgeConfig.kt create mode 100644 jicofo-selector/src/main/kotlin/org/jitsi/jicofo/bridge/BridgeMetrics.kt create mode 100644 jicofo-selector/src/main/kotlin/org/jitsi/jicofo/bridge/BridgeMucDetector.kt create mode 100644 jicofo-selector/src/main/kotlin/org/jitsi/jicofo/bridge/BridgeSelectionStrategy.kt create mode 100644 jicofo-selector/src/main/kotlin/org/jitsi/jicofo/bridge/BridgeSelector.kt create mode 100644 jicofo-selector/src/main/kotlin/org/jitsi/jicofo/bridge/Cascade.kt create mode 100644 jicofo-selector/src/main/kotlin/org/jitsi/jicofo/bridge/HealthCheckListener.kt create mode 100644 jicofo-selector/src/main/kotlin/org/jitsi/jicofo/bridge/IntraRegionBridgeSelectionStrategy.kt create mode 100644 jicofo-selector/src/main/kotlin/org/jitsi/jicofo/bridge/RegionBasedBridgeSelectionStrategy.kt create mode 100644 jicofo-selector/src/main/kotlin/org/jitsi/jicofo/bridge/SingleBridgeSelectionStrategy.kt create mode 100644 jicofo-selector/src/main/kotlin/org/jitsi/jicofo/bridge/SingleMeshTopologyStrategy.kt create mode 100644 jicofo-selector/src/main/kotlin/org/jitsi/jicofo/bridge/SplitBridgeSelectionStrategy.kt create mode 100644 jicofo-selector/src/main/kotlin/org/jitsi/jicofo/bridge/TopologySelectionStrategy.kt create mode 100644 jicofo-selector/src/main/kotlin/org/jitsi/jicofo/bridge/Util.kt create mode 100644 jicofo-selector/src/main/kotlin/org/jitsi/jicofo/bridge/VisitorSelectionStrategy.kt create mode 100644 jicofo-selector/src/main/kotlin/org/jitsi/jicofo/bridge/VisitorTopologyStrategy.kt create mode 100644 jicofo-selector/src/main/kotlin/org/jitsi/jicofo/bridge/colibri/Colibri2Session.kt create mode 100644 jicofo-selector/src/main/kotlin/org/jitsi/jicofo/bridge/colibri/ColibriAllocation.kt create mode 100644 jicofo-selector/src/main/kotlin/org/jitsi/jicofo/bridge/colibri/ColibriSessionManager.kt create mode 100644 jicofo-selector/src/main/kotlin/org/jitsi/jicofo/bridge/colibri/ColibriV2SessionManager.kt create mode 100644 jicofo-selector/src/main/kotlin/org/jitsi/jicofo/bridge/colibri/ConnectSpec.kt create mode 100644 jicofo-selector/src/main/kotlin/org/jitsi/jicofo/bridge/colibri/Exceptions.kt create mode 100644 jicofo-selector/src/main/kotlin/org/jitsi/jicofo/bridge/colibri/Extensions.kt create mode 100644 jicofo-selector/src/main/kotlin/org/jitsi/jicofo/bridge/colibri/ParticipantInfo.kt create mode 100644 jicofo-selector/src/main/resources/reference.conf create mode 100644 jicofo-selector/src/test/kotlin/org/jitsi/jicofo/JicofoSelectorKotestProjectConfig.kt create mode 100644 jicofo-selector/src/test/kotlin/org/jitsi/jicofo/TranscriptionConfigTest.kt create mode 100644 jicofo-selector/src/test/kotlin/org/jitsi/jicofo/TranslationConfigTest.kt create mode 100644 jicofo-selector/src/test/kotlin/org/jitsi/jicofo/bridge/BridgeConfigTest.kt create mode 100644 jicofo-selector/src/test/kotlin/org/jitsi/jicofo/bridge/BridgeReleaseTest.kt create mode 100644 jicofo-selector/src/test/kotlin/org/jitsi/jicofo/bridge/BridgeSelectionStrategyTest.kt create mode 100644 jicofo-selector/src/test/kotlin/org/jitsi/jicofo/bridge/BridgeSelectorTest.kt create mode 100644 jicofo-selector/src/test/kotlin/org/jitsi/jicofo/bridge/BridgeTest.kt create mode 100644 jicofo-selector/src/test/kotlin/org/jitsi/jicofo/bridge/CascadeTest.kt create mode 100644 jicofo-selector/src/test/kotlin/org/jitsi/jicofo/bridge/MaxParticipantsPerIntervalTest.kt create mode 100644 jicofo-selector/src/test/kotlin/org/jitsi/jicofo/bridge/RegionBasedSelectionTest.kt create mode 100644 jicofo-selector/src/test/kotlin/org/jitsi/jicofo/bridge/UtilTest.kt create mode 100644 jicofo-selector/src/test/kotlin/org/jitsi/jicofo/bridge/colibri/ColibriTextTranslationTest.kt create mode 100644 jicofo-selector/src/test/kotlin/org/jitsi/jicofo/bridge/colibri/ColibriTranscriptionTest.kt create mode 100644 jicofo-selector/src/test/kotlin/org/jitsi/jicofo/bridge/colibri/ColibriTranslationTest.kt create mode 100644 jicofo-selector/src/test/kotlin/org/jitsi/jicofo/bridge/colibri/ColibriV2SessionManagerTest.kt create mode 100644 jicofo-selector/src/test/kotlin/org/jitsi/jicofo/bridge/colibri/ConnectSpecTest.kt create mode 100644 jicofo-selector/src/test/kotlin/org/jitsi/jicofo/bridge/colibri/PerSourceTranslatorSpecsTest.kt create mode 100644 jicofo-selector/src/test/kotlin/org/jitsi/jicofo/mock/TestColibri2Server.kt create mode 100644 jicofo/pom.xml create mode 100644 jicofo/spotbugs-exclude.xml create mode 100644 jicofo/src/assembly/archive.xml create mode 100644 jicofo/src/main/java/org/jitsi/jicofo/Main.java create mode 100644 jicofo/src/main/java/org/jitsi/jicofo/auth/AbstractAuthAuthority.java create mode 100644 jicofo/src/main/java/org/jitsi/jicofo/auth/AuthenticationAuthority.java create mode 100644 jicofo/src/main/java/org/jitsi/jicofo/auth/AuthenticationListener.java create mode 100644 jicofo/src/main/java/org/jitsi/jicofo/auth/AuthenticationSession.java create mode 100644 jicofo/src/main/java/org/jitsi/jicofo/auth/ErrorFactory.java create mode 100644 jicofo/src/main/java/org/jitsi/jicofo/auth/ExternalJWTAuthority.java create mode 100644 jicofo/src/main/java/org/jitsi/jicofo/auth/XMPPDomainAuthAuthority.java create mode 100644 jicofo/src/main/java/org/jitsi/jicofo/codec/JingleOfferFactory.java create mode 100644 jicofo/src/main/java/org/jitsi/jicofo/conference/JitsiMeetConference.java create mode 100644 jicofo/src/main/java/org/jitsi/jicofo/conference/JitsiMeetConferenceImpl.java create mode 100644 jicofo/src/main/java/org/jitsi/jicofo/conference/MuteResult.java create mode 100644 jicofo/src/main/java/org/jitsi/jicofo/conference/ParticipantInviteRunnable.java create mode 100644 jicofo/src/main/java/org/jitsi/jicofo/jibri/JibriSession.java create mode 100644 jicofo/src/main/java/org/jitsi/jicofo/util/ErrorResponse.java create mode 100644 jicofo/src/main/java/org/jitsi/jicofo/version/CurrentVersionImpl.java create mode 100644 jicofo/src/main/kotlin/org/jitsi/jicofo/ClientRequirementsConfig.kt create mode 100644 jicofo/src/main/kotlin/org/jitsi/jicofo/ConferenceConfig.kt create mode 100644 jicofo/src/main/kotlin/org/jitsi/jicofo/ConferenceRequest.kt create mode 100644 jicofo/src/main/kotlin/org/jitsi/jicofo/ConferenceStore.kt create mode 100644 jicofo/src/main/kotlin/org/jitsi/jicofo/FocusManager.kt create mode 100644 jicofo/src/main/kotlin/org/jitsi/jicofo/JicofoServices.kt create mode 100644 jicofo/src/main/kotlin/org/jitsi/jicofo/Offer.kt create mode 100644 jicofo/src/main/kotlin/org/jitsi/jicofo/ReinviteMethod.kt create mode 100644 jicofo/src/main/kotlin/org/jitsi/jicofo/auth/AuthConfig.kt create mode 100644 jicofo/src/main/kotlin/org/jitsi/jicofo/bridgeload/LoadRedistributor.kt create mode 100644 jicofo/src/main/kotlin/org/jitsi/jicofo/conference/ClientRequirementsHandler.kt create mode 100644 jicofo/src/main/kotlin/org/jitsi/jicofo/conference/ConferenceMetrics.kt create mode 100644 jicofo/src/main/kotlin/org/jitsi/jicofo/conference/ConferenceUtil.kt create mode 100644 jicofo/src/main/kotlin/org/jitsi/jicofo/conference/JitsiMeetConfig.kt create mode 100644 jicofo/src/main/kotlin/org/jitsi/jicofo/conference/Participant.kt create mode 100644 jicofo/src/main/kotlin/org/jitsi/jicofo/conference/SourceSignaling.kt create mode 100644 jicofo/src/main/kotlin/org/jitsi/jicofo/conference/SourcesToAddOrRemove.kt create mode 100644 jicofo/src/main/kotlin/org/jitsi/jicofo/conference/translation/ConferenceTranslationManager.kt create mode 100644 jicofo/src/main/kotlin/org/jitsi/jicofo/conference/translation/TranslationSourceManager.kt create mode 100644 jicofo/src/main/kotlin/org/jitsi/jicofo/health/HealthConfig.kt create mode 100644 jicofo/src/main/kotlin/org/jitsi/jicofo/health/JicofoHealthChecker.kt create mode 100644 jicofo/src/main/kotlin/org/jitsi/jicofo/jibri/BaseJibri.kt create mode 100644 jicofo/src/main/kotlin/org/jitsi/jicofo/jibri/JibriConfig.kt create mode 100644 jicofo/src/main/kotlin/org/jitsi/jicofo/jibri/JibriDetector.kt create mode 100644 jicofo/src/main/kotlin/org/jitsi/jicofo/jibri/JibriDetectorMetrics.kt create mode 100644 jicofo/src/main/kotlin/org/jitsi/jicofo/jibri/JibriRecorder.kt create mode 100644 jicofo/src/main/kotlin/org/jitsi/jicofo/jibri/JibriSipGateway.kt create mode 100644 jicofo/src/main/kotlin/org/jitsi/jicofo/jibri/JibriStats.kt create mode 100644 jicofo/src/main/kotlin/org/jitsi/jicofo/jigasi/JigasiConfig.kt create mode 100644 jicofo/src/main/kotlin/org/jitsi/jicofo/jigasi/JigasiDetector.kt create mode 100644 jicofo/src/main/kotlin/org/jitsi/jicofo/ktor/Application.kt create mode 100644 jicofo/src/main/kotlin/org/jitsi/jicofo/ktor/RestConfig.kt create mode 100644 jicofo/src/main/kotlin/org/jitsi/jicofo/ktor/exception/Exceptions.kt create mode 100644 jicofo/src/main/kotlin/org/jitsi/jicofo/util/Cancelable.kt create mode 100644 jicofo/src/main/kotlin/org/jitsi/jicofo/util/OfferOptionsUtil.kt create mode 100644 jicofo/src/main/kotlin/org/jitsi/jicofo/util/PreferenceAggregator.kt create mode 100644 jicofo/src/main/kotlin/org/jitsi/jicofo/util/RateLimitedStat.kt create mode 100644 jicofo/src/main/kotlin/org/jitsi/jicofo/visitors/VisitorsConfig.kt create mode 100644 jicofo/src/main/kotlin/org/jitsi/jicofo/xmpp/AuthenticationIqHandler.kt create mode 100644 jicofo/src/main/kotlin/org/jitsi/jicofo/xmpp/AvModerationHandler.kt create mode 100644 jicofo/src/main/kotlin/org/jitsi/jicofo/xmpp/ConferenceIqHandler.kt create mode 100644 jicofo/src/main/kotlin/org/jitsi/jicofo/xmpp/JibriIqHandler.kt create mode 100644 jicofo/src/main/kotlin/org/jitsi/jicofo/xmpp/JigasiIqHandler.kt create mode 100644 jicofo/src/main/kotlin/org/jitsi/jicofo/xmpp/MuteIqHandler.kt create mode 100644 jicofo/src/main/kotlin/org/jitsi/jicofo/xmpp/RoomMetadataHandler.kt create mode 100644 jicofo/src/main/kotlin/org/jitsi/jicofo/xmpp/Smack.kt create mode 100644 jicofo/src/main/kotlin/org/jitsi/jicofo/xmpp/VisitorConnectionMonitor.kt create mode 100644 jicofo/src/main/kotlin/org/jitsi/jicofo/xmpp/VisitorsManager.kt create mode 100644 jicofo/src/main/kotlin/org/jitsi/jicofo/xmpp/XmppServices.kt create mode 100644 jicofo/src/main/kotlin/org/jitsi/jicofo/xmpp/muc/ChatRoomRoleManager.kt create mode 100644 jicofo/src/main/resources/reference.conf create mode 100644 jicofo/src/test/kotlin/org/jitsi/jicofo/ConferenceConfigTest.kt create mode 100644 jicofo/src/test/kotlin/org/jitsi/jicofo/ConferenceIqHandlerTest.kt create mode 100644 jicofo/src/test/kotlin/org/jitsi/jicofo/ConferenceRequestTest.kt create mode 100644 jicofo/src/test/kotlin/org/jitsi/jicofo/DebugStateTest.kt create mode 100644 jicofo/src/test/kotlin/org/jitsi/jicofo/KotestProjectConfig.kt create mode 100644 jicofo/src/test/kotlin/org/jitsi/jicofo/auth/AuthConfigTest.kt create mode 100644 jicofo/src/test/kotlin/org/jitsi/jicofo/auth/XMPPDomainAuthAuthorityTest.kt create mode 100644 jicofo/src/test/kotlin/org/jitsi/jicofo/bridge/BridgePinTest.kt create mode 100644 jicofo/src/test/kotlin/org/jitsi/jicofo/bridgeload/LoadRedistributorTest.kt create mode 100644 jicofo/src/test/kotlin/org/jitsi/jicofo/codec/JingleOfferFactoryTest.kt create mode 100644 jicofo/src/test/kotlin/org/jitsi/jicofo/conference/ClientRequirementsHandlerTest.kt create mode 100644 jicofo/src/test/kotlin/org/jitsi/jicofo/conference/ConferenceTest.kt create mode 100644 jicofo/src/test/kotlin/org/jitsi/jicofo/conference/ConferenceUtilTest.kt create mode 100644 jicofo/src/test/kotlin/org/jitsi/jicofo/conference/ConferenceVisitorsTest.kt create mode 100644 jicofo/src/test/kotlin/org/jitsi/jicofo/conference/IceRestartTest.kt create mode 100644 jicofo/src/test/kotlin/org/jitsi/jicofo/conference/ParticipantInviteRunnableTest.kt create mode 100644 jicofo/src/test/kotlin/org/jitsi/jicofo/conference/source/SourceSignalingTest.kt create mode 100644 jicofo/src/test/kotlin/org/jitsi/jicofo/conference/translation/TranslationSourceManagerTest.kt create mode 100644 jicofo/src/test/kotlin/org/jitsi/jicofo/jibri/JibriDetectorTest.kt create mode 100644 jicofo/src/test/kotlin/org/jitsi/jicofo/jibri/JibriSessionTest.kt create mode 100644 jicofo/src/test/kotlin/org/jitsi/jicofo/jigasi/JigasiSelectorTest.kt create mode 100644 jicofo/src/test/kotlin/org/jitsi/jicofo/mock/ColibriAndJingleXmppConnection.kt create mode 100644 jicofo/src/test/kotlin/org/jitsi/jicofo/mock/ConferenceHarness.kt create mode 100644 jicofo/src/test/kotlin/org/jitsi/jicofo/util/ListConferenceStore.kt create mode 100644 jicofo/src/test/kotlin/org/jitsi/jicofo/util/PreferenceAggregatorTest.kt create mode 100644 jicofo/src/test/kotlin/org/jitsi/jicofo/util/RateLimitedStatTest.kt create mode 100644 jicofo/src/test/kotlin/org/jitsi/jicofo/xmpp/AvModerationHandlerTest.kt create mode 100644 jicofo/src/test/kotlin/org/jitsi/jicofo/xmpp/MuteIqHandlerTest.kt create mode 100644 jicofo/src/test/kotlin/org/jitsi/jicofo/xmpp/RoomMetadataHandlerTest.kt create mode 100644 jicofo/src/test/kotlin/org/jitsi/jicofo/xmpp/VisitorConnectionMonitorTest.kt create mode 100644 jicofo/src/test/kotlin/org/jitsi/jicofo/xmpp/jigasi/JigasiIqHandlerTest.kt create mode 100644 jicofo/src/test/kotlin/org/jitsi/jicofo/xmpp/muc/ChatRoomMemberDiarizeTest.kt create mode 100644 jicofo/src/test/kotlin/org/jitsi/jicofo/xmpp/muc/ChatRoomMemberTranslationLanguageTest.kt create mode 100644 jicofo/src/test/kotlin/org/jitsi/jicofo/xmpp/muc/ChatRoomRoleManagerTest.kt create mode 100644 jicofo/src/test/kotlin/org/jitsi/jicofo/xmpp/muc/ChatRoomTranslationLanguagesTest.kt create mode 100644 lib/logging.properties create mode 100644 pom.xml create mode 100755 resources/build_deb_package.sh create mode 100755 resources/collect-dump-logs.sh create mode 100644 resources/config/jicofo-logrotate.d create mode 100644 resources/jicofo.bat create mode 100755 resources/jicofo.sh create mode 100755 resources/mvn-exec.sh create mode 100644 script/graceful_shutdown.py diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..1c7ee49 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,9 @@ +[*.{kt,kts}] +# Keep constructor parameters wrapped one per line, and kotest specs unindented. +ktlint_standard_class-signature = disabled +max_line_length=120 +ktlint_code_style = intellij_idea + +# I find trailing commas annoying +ktlint_standard_trailing-comma-on-call-site = disabled +ktlint_standard_trailing-comma-on-declaration-site = disabled diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..488f808 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +*.java text eol=lf diff=java diff --git a/.github/ISSUE_TEMPLATE/bug-report.md b/.github/ISSUE_TEMPLATE/bug-report.md new file mode 100644 index 0000000..80cfdc6 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug-report.md @@ -0,0 +1,27 @@ +--- +name: Bug Report +about: Before posting, please make sure you check https://community.jitsi.org + +--- + +*This Issue tracker is only for reporting bugs and tracking code related issues.* + +Before posting, please make sure you check community.jitsi.org to see if the same or similar bugs have already been discussed. General questions, installation help, and feature requests can also be posted to community.jitsi.org. + +## Description +--- + +## Current behavior +--- + +## Expected Behavior +--- + +## Possible Solution +--- + +## Steps to reproduce +--- + +# Environment details +--- diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml new file mode 100644 index 0000000..2afcf6d --- /dev/null +++ b/.github/workflows/maven.yml @@ -0,0 +1,43 @@ +# This workflow will build a Java project with Maven +# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven + +name: Java CI with Maven + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +env: + # Java version to use for the release + RELEASE_JAVA_VERSION: 17 + +jobs: + build: + runs-on: ubuntu-latest + + strategy: + matrix: + java: [ 17, 21 ] + + name: Java ${{ matrix.java }} + + steps: + - name: Checkout + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + + - name: Set up JDK ${{ matrix.java }} + uses: actions/setup-java@c1e323688fd81a25caa38c78aa6df2d33d3e20d9 # v4 + with: + distribution: temurin + java-version: ${{ matrix.java }} + cache: maven + + - name: Build and test with Maven + run: mvn verify -B -Pcoverage + + - name: Upload coverage report + uses: codecov/codecov-action@b9fd7d16f6d7d1b5d2bec1a2887e65ceed900238 # v4 + with: + token: ${{ secrets.CODECOV_TOKEN }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3d852d6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +*.class +/classes/ +/dist/ +*.jar +/junit-reports/ +*.swp +/target/ +jicofo-common/target/ +jicofo-selector/target/ +jicofo/target/ +.classpath +.project +.settings/ +**/.idea/ +*.iml diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..643bcf7 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,159 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Overview + +Jicofo (JItsi COnference FOcus) is a signaling server for Jitsi Meet conferences. It manages XMPP Multi-User Chat (MUC) rooms, initiates Jingle sessions with participants, and coordinates media routing through Jitsi Videobridge instances using the Colibri2 protocol. + +## Build and Test Commands + +### Building +```bash +mvn install # Build all modules and create distribution package +mvn clean install # Clean build +mvn install -DskipTests # Build without running tests +``` + +The distribution package is created in `jicofo/target/jicofo-1.1-SNAPSHOT-archive.zip`. + +### Testing +```bash +mvn test # Run all tests +mvn test -pl jicofo-selector # Run tests for specific module +mvn test -Dtest=BridgeSelectorTest # Run specific test class +``` + +### Code Quality +```bash +mvn exec:exec@ktlint-check # Run ktlint (must be run for individual modules e.g. in ./jicofo-selector/; sibling modules must + # be resolvable, so run "mvn install -DskipTests" from the root first if they are not) +mvn exec:exec@ktlint-format # Auto-format Kotlin code with ktlint (same restriction) +mvn checkstyle:check # Run checkstyle (config: checkstyle.xml) +``` + +## Architecture + +### Module Structure + +This is a multi-module Maven project with three modules: + +1. **jicofo-common** - Shared utilities and data structures + - Conference source management (`ConferenceSourceMap`, `EndpointSourceSet`, `Source`) + - XMPP/MUC abstractions (`ChatRoom`, `ChatRoomMember`, `BaseBrewery`) + - Codec management + - Metrics infrastructure + - No dependencies on other jicofo modules + +2. **jicofo-selector** - Bridge selection and Colibri session management + - Self-contained, reusable component for bridge selection logic + - Bridge management (`Bridge`, `BridgeSelector`, `BridgeMucDetector`) + - Pluggable selection strategies: + - `SingleBridgeSelectionStrategy` - Single bridge, no Octo + - `RegionBasedBridgeSelectionStrategy` - Geographic distribution with Octo + - `IntraRegionBridgeSelectionStrategy` - Load-balanced Octo within regions + - `VisitorSelectionStrategy` - Separate strategies for visitors vs participants + - Colibri2 session management (`ColibriV2SessionManager`, `Colibri2Session`) + - Depends only on jicofo-common + +3. **jicofo** - Main application module + - Entry point: `Main.java` + - Service orchestration: `JicofoServices.kt` (creates and manages all services) + - Conference management: `JitsiMeetConferenceImpl` (Java, ~2000+ lines, core conference logic) + - Focus management: `FocusManager` (conference lifecycle, room JID mapping) + - XMPP services: `XmppServices`, `XmppProvider`, modular IQ handlers + - Authentication: `AbstractAuthAuthority`, `XMPPDomainAuthAuthority`, `ExternalJWTAuthority` + - External service integration: Jibri (recording/streaming), Jigasi (SIP gateway) + - REST API: Ktor-based HTTP interface for health checks, stats, debug endpoints + - Depends on both jicofo-common and jicofo-selector + +### Key Components & Responsibilities + +**Conference Management** +- `JitsiMeetConferenceImpl` - Main conference implementation (manages Jingle sessions, coordinates with bridges, handles MUC membership, lifecycle management) +- `Participant` - Represents a conference participant with Jingle session, sources, and bridge allocation +- `ParticipantInviteRunnable` - Handles participant invitation flow (Jingle session establishment) +- `FocusManager` - Conference lifecycle management, maps room JIDs to conference instances + +**Bridge Selection** +- `BridgeSelector` - Core component for selecting optimal bridges based on health, stress, operational status, and configured strategy +- `Bridge` - Represents a single jitsi-videobridge instance with health and stress tracking +- `BridgeMucDetector` - Discovers bridges via XMPP MUC (brewery pattern) +- `JvbDoctor` - Health check management for bridges + +**Colibri Session Management** +- `ColibriV2SessionManager` - Manages Colibri2 sessions with bridges +- `Colibri2Session` - Represents a session with a single bridge +- Sends `ConferenceModifyIQ` to bridges for allocations and updates + +**XMPP Handling** +- `XmppProvider` - Manages multiple XMPP connections (client, service, visitor) +- Modular IQ handlers: `ConferenceIqHandler`, `JingleIqRequestHandler`, `JibriIqHandler`, etc. +- `VisitorsManager` - Manages visitor functionality across multiple XMPP nodes + +### Application Flow + +1. **Startup**: `Main.java` loads config, creates `JicofoServices`, sets up signal handlers +2. **Conference Creation**: Client requests conference → `FocusManager.conferenceRequest()` → `JitsiMeetConferenceImpl.start()` +3. **Participant Join**: Participant joins MUC → conference detects new member → `ParticipantInviteRunnable` selects bridge, allocates Colibri endpoint, sends Jingle session-initiate +4. **Bridge Selection**: Considers operational status, stress level, graceful shutdown status, version compatibility, and selection strategy +5. **Media Flow**: Participant accepts Jingle session, media flows through videobridge + +### Configuration + +Configuration uses HOCON format (lightbend/config). Main config file is typically `/etc/jitsi/jicofo/jicofo.conf`. + +Reference configuration with all available options: `jicofo-selector/src/main/resources/reference.conf` + +Configuration objects in code are typically Kotlin companion objects (e.g., `BridgeConfig.config`, `ConferenceConfig.config`). + +### Language & Conventions + +- **Kotlin**: New code, utilities, data structures, all test code +- **Java**: Legacy code, notably `JitsiMeetConferenceImpl` (complex conference management) +- **Interop**: Seamless with `@JvmOverloads`, `@JvmField`, companion objects + +### Testing Framework + +- **Primary**: Kotest (BDD-style specs with `ShouldSpec`) +- **Style**: `context("...") { should("...") { ... } }` for behavior-driven tests +- **Mocking**: MockK for Kotlin (`mockk { every { ... } returns ... }`) +- **Test Utilities**: Mock implementations in `jicofo/src/test/kotlin/org/jitsi/jicofo/mock/` + - `MockXmppConnection`, `MockXmppProvider` - XMPP testing + - `MockChatRoom` - MUC simulation + - `TestColibri2Server` - Colibri2 protocol testing +- **Configuration Testing**: Use `withNewConfig { ... }` helper for custom configs +- **Time Control**: `FakeClock` for time-dependent tests +- **Isolation**: Tests use `IsolationMode.InstancePerLeaf` for test isolation +- **Location**: Tests mirror source structure (`src/test/kotlin` matches `src/main/kotlin`) + +### Important Architectural Patterns + +**Separation of Concerns**: Clear module boundaries (common utilities, bridge selection logic, application orchestration) + +**Dependency Injection via Constructor**: Services are composed via constructor injection in `JicofoServices.kt` + +**Strategy Pattern**: Bridge selection strategies and topology strategies are pluggable, configured via HOCON + +**Observer Pattern**: Event-driven architecture with `AsyncEventEmitter` (e.g., `BridgeSelector.EventHandler`, `ColibriSessionManager.Listener`) + +**Thread Safety**: Extensive use of `synchronized` blocks, `ConferenceSourceMap` uses `ConcurrentHashMap`, careful lock ordering to prevent deadlocks + +**Metrics & Observability**: Centralized metrics via `JicofoMetricsContainer`, periodic metric updates, debug state endpoints (`debugState` properties) + +### Key Protocols + +**XMPP**: Communication backbone with multiple connections (Client for participants, Service for bridges, Visitor for visitor nodes) + +**Jingle**: Standard XMPP protocol for peer-to-peer session negotiation (jicofo terminates Jingle signaling, not media) + +**Colibri2**: Modern protocol for bridge communication (endpoint allocation, management, relay support for Octo) + +**Brewery Pattern**: Service discovery - bridges/Jibris/Jigasis join specific MUC rooms, jicofo monitors presence + +### Notable Design Decisions + +- **Graceful Degradation**: Bridges report stress levels, can enter graceful shutdown, health checks with timeout-based recovery +- **Scalability**: Multi-bridge support (Octo mesh), region-based selection, visitor architecture for large conferences, load redistribution +- **Singleton with Synchronization**: `JicofoServices` uses synchronized singleton pattern (required because services start threads that depend on the singleton) +- **Legacy Code Preservation**: `JitsiMeetConferenceImpl` remains in Java with extensive synchronization (noted as needing refactoring, but stable) diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README.md b/README.md new file mode 100644 index 0000000..68cf4f9 --- /dev/null +++ b/README.md @@ -0,0 +1,104 @@ +# Jicofo + +JItsi COnference FOcus is a signaling server, one of the backend components in the [Jitsi Meet] stack. + +[Jitsi Meet]: https://github.com/jitsi/jitsi-meet + +## Overview + +Jitsi Meet conferences are associated with an XMPP +[Multi-User Chat (MUC) room](https://xmpp.org/extensions/xep-0045.html). The MUC functionality is provided by the +XMPP server (prosody). + +Jicofo joins the conference MUC and is then responsible for initiating a +[Jingle](https://xmpp.org/extensions/xep-0166.html) session with each participant (in this sense it is the "focus" of the +conference, which is where its name comes from). While Jicofo manages and terminates Jingle sessions, it does not +process any of the media (audio/video). Instead, it uses one or more +[Jitsi Videobridge](github.com/jitsi/jitsi-videobridge/) instances. + +Jicofo is responsible for selecting a Jitsi Videobridge for each participant, and manages the set of videobridges for +the conference with the COLIBRI protocol (colibri version 2 is now used, the format in XEP-0340 is now deprecated). + +In general the conference participants and videobridge instances are accessed through different XMPP connections -- +the configured Client and Service connections, respectively, though they may coincide. + +![Connection between Jicofo and the other components in the Jitsi Meet stack.](https://github.com/jitsi/jicofo/blob/master/doc/diagram.png?raw=true) + +# Configuration +Jicofo takes its configuration from a [hocon](https://github.com/lightbend/config/blob/main/HOCON.md) config file, +usually installed in `/etc/jitsi/jicofo/jicofo.conf`. See the +[reference.conf](https://github.com/jitsi/jicofo/blob/master/jicofo-selector/src/main/resources/reference.conf) file +for the available options. + +# Installation +## Debian + +The recommended way to install Jicofo and Jitsi Meet is to follow the +[Quick Install Guide](https://github.com/jitsi/jitsi-meet/blob/master/doc/quick-install.md) for debian-based systems. + +### Binaries + +You can download Debian/Ubuntu binaries here: +* [stable](https://download.jitsi.org/stable/) ([instructions](https://jitsi.org/downloads/ubuntu-debian-installations-instructions/)) +* [testing](https://download.jitsi.org/testing/) ([instructions](https://jitsi.org/downloads/ubuntu-debian-installations-instructions-for-testing/)) +* [nightly](https://download.jitsi.org/unstable/) ([instructions](https://jitsi.org/downloads/ubuntu-debian-installations-instructions-nightly/)) + +## Docker +Jicofo is available as a docker image as part of [docker-jitsi-meet](https://github.com/jitsi/docker-jitsi-meet). + +## Manual +This section is only required for a manual setup, not necessary when using Quick Install or other methods. + +### Prosody configuration + +Jicofo needs privileges to create Multi-User Chat rooms. To grant these privileges we create an account for it and add +it to the global `admins` list. We create a new virtual host, because the one used by clients only supports anonymous +authentication. We add this to Prosody's config file (`/etc/prosody/prosody.cfg.lua` by default): +``` +admins = { focus@auth.jitsi.example.com } +VirtualHost "auth.jitsi.example.com" + authentication = "internal_hashed" +``` +Then restart Prosody and create the user account: +``` +sudo prosodyctl restart +sudo prosodyctl register focus auth.jitsi.example.com focuspassword +``` + +### Building Jicofo +Build using maven with: +```commandline +mvn install +``` + +This will create a package in `jicofo/target/jicofo-1.1-SNAPSHOT-archive.zip` +### Running Jicofo +Extract the distribution package and run with `jicofo.sh`. + +### Certificates +Jicofo uses an XMPP user connection (on port 5222 by default), and since the +upgrade to smack4 it verifies the server's certificate. In a default +installation the debian installation scripts take care of generating a +self-signed certificate and adding it to the keystore. + +For situations in which the certificate is not trusted you can add it to the +store by: + +#### On Linux +``` +sudo cp cert.pem /usr/local/share/ca-certificates/ +sudo update-ca-certificates +``` + +#### On MacOS X +On Mac java uses its own keystore, so adding the certificate to the system one +does not work. Add it to the java keystore with: +``` +sudo keytool -importcert -file cert.pem -keystore /Library/Java//JavaVirtualMachines/jdk1.8.0_25.jdk/Contents/Home/jre/lib/security/cacerts +``` + +Note that if the XMPP server you are connecting to is a prosody instance +configured with the jitsi-meet scripts, then you can find the certificate in: +``` +/var/lib/prosody/$JICOFO_AUTH_DOMAIN.crt +``` diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..cf2773d --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,9 @@ +# Security + +## Reporting security issues + +We take security very seriously and develop all Jitsi projects to be secure and safe. + +If you find (or simply suspect) a security issue in any of the Jitsi projects, please report it to us via [HackerOne](https://hackerone.com/8x8-bounty) or send us an email to security@jitsi.org. + +**We encourage responsible disclosure for the sake of our users, so please reach out before posting in a public space.** diff --git a/checkstyle.xml b/checkstyle.xml new file mode 100644 index 0000000..9690b13 --- /dev/null +++ b/checkstyle.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/debian/README.Debian b/debian/README.Debian new file mode 100644 index 0000000..14e5057 --- /dev/null +++ b/debian/README.Debian @@ -0,0 +1,5 @@ +jicofo for Debian +--------------------- + +If you want a WebRTC videoconferencing solution you have to install jitsi-meet +and jitsi-videobridge packages which uses Jitsi Videobridge as a SFU (Selective Forwarding Unit) diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 0000000..ddf95cb --- /dev/null +++ b/debian/changelog @@ -0,0 +1,6 @@ +jicofo (1.0-0-g9fc93ed-1) unstable; urgency=low + + * Initial release. + + -- Damian Minkov Fri, 10 Oct 2017 16:18:00 +0000 + diff --git a/debian/compat b/debian/compat new file mode 100644 index 0000000..48082f7 --- /dev/null +++ b/debian/compat @@ -0,0 +1 @@ +12 diff --git a/debian/control b/debian/control new file mode 100644 index 0000000..262338a --- /dev/null +++ b/debian/control @@ -0,0 +1,14 @@ +Source: jicofo +Section: net +Priority: extra +Maintainer: Jitsi Team +Uploaders: Emil Ivov , Damian Minkov , Pawel Domas +Build-Depends: debhelper, openjdk-17-jdk | openjdk-21-jdk, maven +Standards-Version: 3.9.3 +Homepage: https://jitsi.org/meet + +Package: jicofo +Architecture: all +Depends: ${misc:Depends}, openjdk-17-jre-headless | openjdk-17-jre | openjdk-21-jre-headless | openjdk-21-jre, ruby-hocon, jq +Description: JItsi Meet COnference FOcus + Jicofo is a conference focus for Jitsi Meet application. diff --git a/debian/copyright b/debian/copyright new file mode 100644 index 0000000..8808ac4 --- /dev/null +++ b/debian/copyright @@ -0,0 +1,13 @@ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: Jitsi Conference Focus +Upstream-Contact: Emil Ivov +Source: https://github.com/jitsi/jicofo + +Files: * +Copyright: 2013-2018 Jitsi +License: Apache 2.0 + +License: Apache 2.0 +On Debian systems, the full text of the Apache licence +version 2.0 can be found in the file +'/usr/share/common-licenses/Apache-2.0'. diff --git a/debian/docs b/debian/docs new file mode 100644 index 0000000..e69de29 diff --git a/debian/init.d b/debian/init.d new file mode 100644 index 0000000..41387ad --- /dev/null +++ b/debian/init.d @@ -0,0 +1,101 @@ +#! /bin/sh +# +# INIT script for Jitsi Conference Focus +# Version: 1.0 4-Dec-2014 pawel.domas@jitsi.org +# +### BEGIN INIT INFO +# Provides: jicofo +# Required-Start: $local_fs $remote_fs +# Required-Stop: $local_fs $remote_fs +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: Jitsi conference Focus +# Description: Conference focus for Jitsi Meet application. +### END INIT INFO + +. /lib/lsb/init-functions + +# Include jicofo defaults if available +if [ -f /etc/jitsi/jicofo/config ]; then + . /etc/jitsi/jicofo/config +fi + +PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin +DAEMON=/usr/share/jicofo/jicofo.sh +DAEMON_DIR=/usr/share/jicofo/ +NAME=jicofo +USER=jicofo +PIDFILE=/var/run/jicofo.pid +LOGFILE=/var/log/jitsi/jicofo.log +DESC=jicofo + + +if [ ! -x $DAEMON ] ;then + echo "Daemon not executable: $DAEMON" + exit 1 +fi + +set -e + +stop() { + if [ -f $PIDFILE ]; then + PID=$(cat $PIDFILE) + fi + echo -n "Stopping $NAME: " + if [ $PID ]; then + kill $PID || true + rm $PIDFILE || true + echo "$NAME stopped." + else + echo "$NAME doesn't seem to be running." + fi +} + +start() { + if [ -f $PIDFILE ]; then + echo "$NAME seems to be already running, we found pidfile $PIDFILE." + exit 1 + fi + echo -n "Starting $NAME: " + export JICOFO_AUTH_PASSWORD JICOFO_MAX_MEMORY + start-stop-daemon --start --quiet --background --chuid $USER --make-pidfile --pidfile $PIDFILE \ + --exec /bin/bash -- -c "cd $DAEMON_DIR; JAVA_SYS_PROPS=\"$JAVA_SYS_PROPS\" exec $DAEMON $JICOFO_OPTS < /dev/null >> $LOGFILE 2>&1" + echo "$NAME started." +} + +reload() { + echo 'Not yet implemented.' +} + +status() { + status_of_proc -p $PIDFILE java "$NAME" && exit 0 || exit $? +} + +case "$1" in + start) + start + ;; + stop) + stop + ;; + restart) + stop + start + ;; + reload) + reload + ;; + force-reload) + reload + ;; + status) + status + ;; + *) + N=/etc/init.d/$NAME + echo "Usage: $N {start|stop|restart|reload|status}" >&2 + exit 1 + ;; +esac + +exit 0 diff --git a/debian/install b/debian/install new file mode 100644 index 0000000..e6fd2ca --- /dev/null +++ b/debian/install @@ -0,0 +1,6 @@ +jicofo/target/dependency/* usr/share/jicofo/lib +lib/logging.properties etc/jitsi/jicofo +jicofo.jar usr/share/jicofo +resources/jicofo.sh usr/share/jicofo +resources/collect-dump-logs.sh usr/share/jicofo +jicofo/jicofo etc/logrotate.d/ diff --git a/debian/manpage.1 b/debian/manpage.1 new file mode 100644 index 0000000..a2cb1e6 --- /dev/null +++ b/debian/manpage.1 @@ -0,0 +1,38 @@ +.\" Hey, EMACS: -*- nroff -*- +.\" First parameter, NAME, should be all caps +.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection +.\" other parameters are allowed: see man(7), man(1) +.TH JICOFO SECTION "November 28, 2014" +.\" Please adjust this date whenever revising the manpage. +.\" +.\" Some roff macros, for reference: +.\" .nh disable hyphenation +.\" .hy enable hyphenation +.\" .ad l left justify +.\" .ad b justify to both left and right margins +.\" .nf disable filling +.\" .fi enable filling +.\" .br insert line break +.\" .sp insert n+1 empty lines +.\" for manpage-specific macros, see man(7) +.SH NAME +jicofo \- conference focus for Jitsi Meet application. +.SH SYNOPSIS +.B /usr/share/jicofo/jicofo.sh +.RI [ options ] ... +.br +.B /etc/init.d/jicofo (start|stop|restart) +.SH DESCRIPTION +This manual page documents briefly the +.B jicofo +service. +.PP +.\" TeX users may be more comfortable with the \fB\fP and +.\" \fI\fP escape sequences to invode bold face and italics, +.\" respectively. +\fBjicofo\fP is a conference focus for Jitsi Meet application. +.SH AUTHOR +jicofo was written by Jitsi.org and Bluejimp.com. +.PP +This manual page was written by Pawel Domas , +for the Debian project (and may be used by others). diff --git a/debian/po/POTFILES.in b/debian/po/POTFILES.in new file mode 100644 index 0000000..98e8416 --- /dev/null +++ b/debian/po/POTFILES.in @@ -0,0 +1 @@ +[type: gettext/rfc822deb] templates \ No newline at end of file diff --git a/debian/po/templates.pot b/debian/po/templates.pot new file mode 100644 index 0000000..9aedf0b --- /dev/null +++ b/debian/po/templates.pot @@ -0,0 +1,54 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the jicofo package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: jicofo\n" +"Report-Msgid-Bugs-To: jicofo@packages.debian.org\n" +"POT-Creation-Date: 2016-11-21 19:43+0000\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: 8bit\n" + +#. Type: string +#. Description +#: ../templates:1001 +msgid "Hostname:" +msgstr "" + +#. Type: string +#. Description +#: ../templates:1001 +msgid "The jicofo package needs the DNS hostname of your instance." +msgstr "" + +#. Type: string +#. Description +#: ../templates:2001 +msgid "Jicofo username:" +msgstr "" + +#. Type: string +#. Description +#: ../templates:2001 +msgid "The jicofo needs an authenticated admin user to connect to xmpp server." +msgstr "" + +#. Type: password +#. Description +#: ../templates:3001 +msgid "Jicofo user password:" +msgstr "" + +#. Type: password +#. Description +#: ../templates:3001 +msgid "The secret used to connect to xmpp server as jicofo user." +msgstr "" diff --git a/debian/postinst b/debian/postinst new file mode 100644 index 0000000..91c4801 --- /dev/null +++ b/debian/postinst @@ -0,0 +1,167 @@ +#!/bin/bash +# postinst script for jicofo + +set -e + +case "$1" in + configure) + + CONFIG="/etc/jitsi/jicofo/config" + + # we don't want to regenerate config on upgrade + OLDCONFIG="false" + if [ -f $CONFIG ]; then + . $CONFIG + if [ -n "$JICOFO_HOSTNAME" ] \ + && [ -n "$JICOFO_AUTH_DOMAIN" ] && [ -n "$JICOFO_AUTH_USER" ] && [ -n "$JICOFO_AUTH_PASSWORD" ]; then + OLDCONFIG="true" + fi + fi + + # debconf hostname question + . /usr/share/debconf/confmodule + + # try to get host from jitsi-videobridge + db_get jitsi-videobridge/jvb-hostname + if [ -z "$RET" ] ; then + # server hostname + db_set jitsi-videobridge/jvb-hostname "localhost" + db_input critical jitsi-videobridge/jvb-hostname || true + db_go + fi + JVB_HOSTNAME=$(echo "$RET" | xargs echo -n) + + # generate config on new install + if [ "$OLDCONFIG" = "false" ] || [ "$JVB_HOSTNAME" != "$JICOFO_HOSTNAME" ]; then + + JICOFO_HOSTNAME="$JVB_HOSTNAME" + + JICOFO_AUTH_DOMAIN="auth.$JICOFO_HOSTNAME" + JICOFO_AUTH_USER="focus" + db_get jicofo/jicofo-authpassword + if [ -z "$RET" ] ; then + RET=`cat /dev/urandom | tr -dc 'a-zA-Z0-9' | head -c 16` + fi + JICOFO_AUTH_PASSWORD="$RET" + + # This re-creates $CONFIG. If previous values existed, they will be set in jicofo.conf below. + echo '# adds java system props that are passed to jicofo (default are for home and logging config file)' > $CONFIG + echo "JAVA_SYS_PROPS=\"-Dnet.java.sip.communicator.SC_HOME_DIR_LOCATION=/etc/jitsi\ + -Dnet.java.sip.communicator.SC_HOME_DIR_NAME=jicofo\ + -Dnet.java.sip.communicator.SC_LOG_DIR_LOCATION=/var/log/jitsi\ + -Djava.util.logging.config.file=/etc/jitsi/jicofo/logging.properties\"" >> $CONFIG + + fi + + # Store values, so we can use it if we are configuring other packages on the same machine + # we are currently doing this on every upgrade in order to be able to successfully upgrade + # from old packages, otherwise we can do it only on new configuration or reconfiguration + db_set jicofo/jicofo-authpassword "$JICOFO_AUTH_PASSWORD" + + # and we're done with debconf + db_stop + + OLD_JITSI_CONFIG="/etc/jitsi/jicofo/sip-communicator.properties" + HOCON_CONFIG="/etc/jitsi/jicofo/jicofo.conf" + + if [ ! -f $HOCON_CONFIG ]; then + echo "Generating an empty jicofo.conf file" + echo "# Jicofo HOCON configuration. See reference.conf in /usr/share/jicofo/jicofo.jar for +#available options, syntax, and default values. +jicofo { +}" >> $HOCON_CONFIG + fi + + # Make sure the client-proxy is set correctly. + hocon -f $HOCON_CONFIG set jicofo.xmpp.client.client-proxy "\"focus.$JVB_HOSTNAME\"" + + # Set the client XMPP config in jicofo.conf. The values were either read from an existing + # /etc/jitsi/jicofo/config or generated above. + if ! hocon -f $HOCON_CONFIG get "jicofo.xmpp.client.xmpp-domain" > /dev/null 2>&1 ;then + hocon -f $HOCON_CONFIG set "jicofo.xmpp.client.xmpp-domain" "\"$JICOFO_HOSTNAME\"" + fi + if ! hocon -f $HOCON_CONFIG get "jicofo.xmpp.client.domain" > /dev/null 2>&1 ;then + hocon -f $HOCON_CONFIG set "jicofo.xmpp.client.domain" "\"$JICOFO_AUTH_DOMAIN\"" + fi + if ! hocon -f $HOCON_CONFIG get "jicofo.xmpp.client.username" > /dev/null 2>&1 ;then + hocon -f $HOCON_CONFIG set "jicofo.xmpp.client.username" "\"$JICOFO_AUTH_USER\"" + fi + if ! hocon -f $HOCON_CONFIG get "jicofo.xmpp.client.password" > /dev/null 2>&1 ;then + hocon -f $HOCON_CONFIG set "jicofo.xmpp.client.password" "\"$JICOFO_AUTH_PASSWORD\"" + fi + + # Add the JvbBrewery config if missing + if ! grep -sq "org.jitsi.jicofo.BRIDGE_MUC" "$OLD_JITSI_CONFIG" ;then + if ! hocon -f $HOCON_CONFIG get "jicofo.bridge.brewery-jid" > /dev/null 2>&1 ; then + hocon -f $HOCON_CONFIG set "jicofo.bridge.brewery-jid" \"JvbBrewery@internal.auth.${JVB_HOSTNAME}\" + fi + fi + + # Mark the jibri domain as trusted unless the trusted-domains have been set. + if [[ $(hocon -j -f $HOCON_CONFIG get "jicofo.xmpp.trusted-domains" 2> /dev/null | jq -r "type") != "array" ]] ;then + hocon -f $HOCON_CONFIG set "jicofo.xmpp.trusted-domains" "[ \"recorder.$JVB_HOSTNAME\" ]" + fi + + # we don't want to start the daemon as root + if ! getent group jitsi > /dev/null ; then + groupadd jitsi + fi + if ! getent passwd jicofo > /dev/null ; then + useradd -r -g jitsi --shell /bin/bash --create-home -d /usr/share/jicofo jicofo + fi + if ! groups jicofo | grep '\bjitsi\b' > /dev/null ; then + usermod -g jitsi jicofo + fi + + mkdir -p /usr/share/jicofo + + # we claim the home folder of jicofo in case it is owned by someone else + OWNER=$(stat -c '%U' /usr/share/jicofo) + GROUP=$(stat -c '%G' /usr/share/jicofo) + if ! dpkg-statoverride --list /usr/share/jicofo/* >/dev/null && [ "$OWNER:$GROUP" != "jicofo:jitsi" ]; then + chown -R jicofo:jitsi /usr/share/jicofo + OWNER=jicofo + GROUP=jitsi + fi + + CONFIG_DIR=$(dirname $CONFIG) + if ! dpkg-statoverride --list "$CONFIG_DIR" >/dev/null; then + chown -R jicofo:jitsi "$CONFIG_DIR" + chmod 750 "$CONFIG_DIR" + fi + + # die logz + if [ ! -d /var/log/jitsi ]; then + mkdir -p /var/log/jitsi + chown $OWNER:$GROUP /var/log/jitsi + chmod 770 /var/log/jitsi + fi + ls /var/log/jitsi/jicofo* 1>/dev/null 2>&1 && chown -f -R $OWNER:$GROUP /var/log/jitsi/jicofo* + ls /var/log/jitsi/jicofo* 1>/dev/null 2>&1 && chmod -f -R 640 /var/log/jitsi/jicofo* + + # ensure focus is not running - it will be started at the end + if invoke-rc.d jicofo status >/dev/null 2>&1 ; then + invoke-rc.d jicofo stop || true + fi + + # clean up old jicofo group + if getent group jicofo > /dev/null; then + groupdel jicofo + fi + ;; + + abort-upgrade|abort-remove|abort-deconfigure) + ;; + + *) + echo "postinst called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + +# dh_installdeb will replace this with shell code automatically +# generated by other debhelper scripts. + +#DEBHELPER# + +exit 0 diff --git a/debian/postrm b/debian/postrm new file mode 100644 index 0000000..0f63fb6 --- /dev/null +++ b/debian/postrm @@ -0,0 +1,57 @@ +#!/bin/bash +# postrm script for jicofo + +set -e + +# Load debconf +. /usr/share/debconf/confmodule + + +case "$1" in + purge) + + # Clear the debconf variable + db_purge + + # clear user and group + # ensure nothing is running as jicofo user + if ps -u jicofo h; then + `ps -u jicofo -o pid h | xargs kill` + fi + # and then remove the user + if getent passwd jicofo > /dev/null ; then + deluser jicofo + fi + + # clear logs + if [ -f "/var/log/jitsi/jicofo.log" ]; then + rm -f "/var/log/jitsi/jicofo.log" + fi + + rm -rf /usr/share/jicofo/ + rm -f /etc/jitsi/jicofo/config + rm -f /etc/jitsi/jicofo/sip-communicator.properties + rm -f /etc/jitsi/jicofo/logging.properties + rm -f /etc/jitsi/jicofo/jicofo.conf + ;; + + remove) + ;; + + upgrade|failed-upgrade|abort-install|abort-upgrade|disappear) + ;; + + *) + echo "postrm called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + +# dh_installdeb will replace this with shell code automatically +# generated by other debhelper scripts. + +#DEBHELPER# + +db_stop + +exit 0 diff --git a/debian/preinst b/debian/preinst new file mode 100644 index 0000000..4074d51 --- /dev/null +++ b/debian/preinst @@ -0,0 +1,14 @@ +#!/bin/bash +# preinst script for jicofo + +set -e +echo "Running preinst $@" + +if [ "$1" == "upgrade" ] ;then + if [ -d /etc/logrotate.d/jicofo ] ;then + # An earlier version of the package incorrectly installed a directory in logrotate.d. If that's the case + # remove it, so the file can be properly installed. + echo "/etc/logrotate.d/jicofo is a directory, removing." + rm -rf /etc/logrotate.d/jicofo + fi +fi diff --git a/debian/rules b/debian/rules new file mode 100755 index 0000000..f328a46 --- /dev/null +++ b/debian/rules @@ -0,0 +1,30 @@ +#!/usr/bin/make -f + +# Uncomment this to turn on verbose mode. +#export DH_VERBOSE=1 +#export DEB_VERBOSE_ALL=true + +%: + dh $@ + +override_dh_auto_clean: + mvn -Dmaven.repo.local=${HOME}/.m2/repository clean + +override_dh_auto_configure: + # do nothing + +override_dh_auto_test: + # do nothing + +override_dh_auto_build: + mvn -Dmaven.repo.local=${HOME}/.m2/repository -DskipTests -Dassembly.skipAssembly=true package + mvn -Dmaven.repo.local=${HOME}/.m2/repository dependency:copy-dependencies -DincludeScope=runtime + +override_dh_install: + mkdir -p debian/tmp/jicofo + cp resources/config/jicofo-logrotate.d debian/tmp/jicofo/jicofo + cp jicofo/target/jicofo*.jar debian/tmp/jicofo.jar + dh_install + +override_dh_auto_install: + # do nothing diff --git a/debian/source/format b/debian/source/format new file mode 100644 index 0000000..c3d9f24 --- /dev/null +++ b/debian/source/format @@ -0,0 +1,2 @@ +3.0 (quilt) + diff --git a/debian/templates b/debian/templates new file mode 100644 index 0000000..dc46006 --- /dev/null +++ b/debian/templates @@ -0,0 +1,9 @@ +Template: jitsi-videobridge/jvb-hostname +Type: string +_Description: Hostname: + The jicofo package needs the DNS hostname of your instance. + +Template: jicofo/jicofo-authpassword +Type: password +_Description: Jicofo user password: + The secret used to connect to xmpp server as jicofo user. diff --git a/doc/conference-request.md b/doc/conference-request.md new file mode 100644 index 0000000..83bc756 --- /dev/null +++ b/doc/conference-request.md @@ -0,0 +1,61 @@ +# +Conference-request refers to an initial exchange between a client and jicofo, which happens before the client joins +the conference MUC. Its purposes are to: +1. Notify jicofo that it should join a certain MUC. +2. Have the client block until jicofo has created/joined the MUC (in case the client is not allowed to create a MUC). +3. Allow the first client in a conference to specify certain conference-wide options to jicofo. +4. Allow jicofo to redirect the client to a visitor node. +5. Allow external authentication with an XMPP domain or [Shibboleth](./shibboleth.md) + +# Format +The request and response share a similar format. + +## HTTP format +For HTTP, the conference request is encoded as JSON and POSTed to /conference-request/v1. Note that authentication is +not supported over HTTP due to the lack of an associated JID. + +The reason HTTP support was added is to prevent unnecessary connections to prosody when visitor redirection is used +(i.e. a visitor sends an HTTP request, then logs into the XMPP server specified in the response) + +### Example request +```js +{ + "room": "conferenceName@example.com", // The JID of the conference + "properties": { + "rtcstatsEnabled": true + } +} +``` +### Example response +```js +{ + "ready": true, // Jicofo has joined the room and the client can proceed. + "focusJid": "jicofo@v1.example.com", // Indicates jicofo's JID, which can be trusted by the client + "vnode": "v1", // Redirect to a visitor node with id "v1" + "properties": { + "sipGatewayEnabled": true // Signal jigasi support + } +} +``` + +## XMPP format +For XMPP, the conference request is encoded in a +[ConferenceIq](https://github.com/jitsi/jitsi-xmpp-extensions/blob/master/src/main/java/org/jitsi/xmpp/extensions/jitsimeet/ConferenceIq.java). + +### Example request +```xml + + + + + +``` + +### Example response +```xml + + + " + + +``` diff --git a/doc/diagram.key b/doc/diagram.key new file mode 100755 index 0000000000000000000000000000000000000000..6e0192b124d5c667d1fc6e816e83afcd31953612 GIT binary patch literal 1647051 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

(=n=t+Ycb` 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

paiqiMC+j}-(ovWe$ z$QVh09lM)+13|m!Y=(%Gb5XOJ9C^bm6Vfmf-kj!Qm#X6@O7&xtuJ~4=3kHpA!MgU> zu3HLfoQxh)RaBMq{h;r{t_>!4u;5~6CEFgBW4&k05}qj^Q1?50hi5-6b*@xjp|QZ;H^Qx=rc(E7Yr)+%zoi#5x&V>(HC< zzt9K>EP-+j5swh=z$iompq%HmGO`{jpwrm9k|yO^P{$s=mun$Y3GOrC4X8@A6|9oDk>@>qQEWy@1j2q>$m>(A6aNs z2Q|J`3u$XWzdRP&q!&Pb&F-&3?)#x3>y;{omBBRxZ4-Blm+IL2pC)#SV`h0iqWcYH zlNX1Zakas6_a_WfQ`i>aa|1mfKBpW}46&-%e%oBEkovBBTLgxcSbGjxbD5OmDlO0Gh=G*v)znV8don=f($-n3f+qW)18pWYubQp_7$t>9=bBkp*~o=#1Y*3ctfQ z+N>8VaIyDPQi$`EHYe}xHZoi}mjxWg5EaBa-xFhs%_Qm<@>ZNQRsMt&uZ_onY2*ye z%`->Ly>c5qWS|q3`sE$Ob%$0w#3p5}zpPTOXE#EMSFL`Wa6`mv@9Hj5&lzOhg7J7k zz;JnZ9k}78YMvRHZuXmQ2MXXH+Vx2GF_6E#4rKnpTL|Ki?R^T9i(P$~BB0$b>7Q6PR6@nfbmpC-4gz`g8SP9Qb(^k}cx9};&`2FWbIuWhu z(&X0_s5vjRI01L;4mGl2JPvpnj-;+x?sl=C8hsIAt7i0;wvTmI;v>3AY0vu5dzrbm7{Rr=UmN%vRn`?ubSP}WXr zO}D^E4ng$5{)hk6opF~w zn+q9f3h-iYqz(-|tfu@{ms|kz<3yy@sY!yvtgn%{pre68pX%uy`sqDCmS|sQa`LV5 z+|qGif_ECDny)mu%H8K|w%_r+aCY~e24V++6Mamw*~dlfA6eeuXbi{Bt@7?T+-`E< z-1_k+=p6zrjMv3V20xd#2fY_Rd{>z*_U#CL`_{^uAbTw}a|O+@^wYvk5w|x!L|`)w zOKhKuAQ%+-8!a!5I2A81Em6BYKj!9XQ07zQZ6YYb=8u8OywfD+%^AlSlQ5Q7^$bTW$Y05Au&>1-3*vsC&(B_6~V;|s=jSG$@z6u zI<_oY4NQ5gK+ZZGMN3+JIvRHK9L~N0=hILtW;_hMmfi3Y57XB(N8WZ;NaGW9G1vXD zVg83d?0RKEnDxq|2`$Dtkq#XpdB}W+U>zm`eDf3@hpeOjdIbPA96E8mT6SLrh420t zHM%z|_lk4qBARm>nFC$`ymAmUI8=GAk0BN)8=`>G2_s(lCa>h)Ne@>8LJoB?VOf-s zwl8+4SuaJJ+^3C6ATM#WCjo-@0V-o&;!l{XJL(O7A8j0d`VpfLr7-^|kT1GOKjDLBLIFj{;@dn*0^OkORmL;c@uY{(#p`@$iX0qx>BYRPS)Z>?bw->k`no>RN-NF<`8`R(f#3uk($X*!#m8Av$m@B`9O=R@w zxQlREdwa(ws6}MlD1j5VnJGjNtHyK=6*il-Ju zi(Va7SB)aTK{+|_R~P&1CNB(aIMb1ox7!H4gRf*=`R3vQI3?YKYy!(ND$C62G=-ON zi^&ti%!dLpDVrp7=q#*Osy_s#(MGI1kX`p)U#J>2`-{?<8eb>AP%xKqEn$1(*w