diff --git a/mailbox-cli/src/main/java/org/briarproject/mailbox/cli/Main.kt b/mailbox-cli/src/main/java/org/briarproject/mailbox/cli/Main.kt index a6fc20be8def590faa9434dc26ba5b3e48d877b0..6dd40c126530145068fe8e719c9e120e708a6d03 100644 --- a/mailbox-cli/src/main/java/org/briarproject/mailbox/cli/Main.kt +++ b/mailbox-cli/src/main/java/org/briarproject/mailbox/cli/Main.kt @@ -25,6 +25,8 @@ import com.github.ajalt.clikt.core.CliktCommand import com.github.ajalt.clikt.parameters.options.counted import com.github.ajalt.clikt.parameters.options.flag import com.github.ajalt.clikt.parameters.options.option +import kotlinx.coroutines.flow.takeWhile +import kotlinx.coroutines.runBlocking import org.briarproject.mailbox.core.CoreEagerSingletons import org.briarproject.mailbox.core.JavaCliEagerSingletons import org.briarproject.mailbox.core.db.TransactionManager @@ -33,6 +35,7 @@ import org.briarproject.mailbox.core.setup.QrCodeEncoder import org.briarproject.mailbox.core.setup.SetupManager import org.briarproject.mailbox.core.setup.WipeManager import org.briarproject.mailbox.core.system.InvalidIdException +import org.briarproject.mailbox.core.tor.TorPlugin import org.slf4j.LoggerFactory.getLogger import java.util.logging.Level.ALL import java.util.logging.Level.INFO @@ -77,6 +80,9 @@ class Main : CliktCommand( @Inject internal lateinit var wipeManager: WipeManager + @Inject + internal lateinit var torPlugin: TorPlugin + @Inject internal lateinit var qrCodeEncoder: QrCodeEncoder @@ -138,11 +144,20 @@ class Main : CliktCommand( setupManager.getOwnerToken(txn) != null } if (!ownerTokenExists) { - // TODO remove before release - val token = setupToken ?: db.read { setupManager.getSetupToken(it) } - println("curl -v -H \"Authorization: Bearer $token\" -X PUT http://localhost:8000/setup") - // FIXME: We need to wait for the hidden service address to become available + if (debug) { + val token = setupToken ?: db.read { setupManager.getSetupToken(it) } + println( + "curl -v -H \"Authorization: Bearer $token\" -X PUT " + + "http://localhost:8000/setup" + ) + } // If not set up, show QR code for manual setup + runBlocking { + // wait until Tor becomes active and published the onion service + torPlugin.state.takeWhile { state -> + state != TorPlugin.State.ACTIVE + } + } qrCodeEncoder.getQrCodeBitMatrix()?.let { println(QrCodeRenderer.getQrString(it)) } diff --git a/mailbox-core/src/main/java/org/briarproject/mailbox/core/tor/TorPlugin.java b/mailbox-core/src/main/java/org/briarproject/mailbox/core/tor/TorPlugin.java index ab2bbd79673fdc25445e7dbae238bd415e5d5426..73f881b47dfd9e931d5fc0b0db9809e0c6d0a59e 100644 --- a/mailbox-core/src/main/java/org/briarproject/mailbox/core/tor/TorPlugin.java +++ b/mailbox-core/src/main/java/org/briarproject/mailbox/core/tor/TorPlugin.java @@ -59,10 +59,14 @@ import javax.annotation.Nullable; import javax.annotation.concurrent.GuardedBy; import javax.annotation.concurrent.ThreadSafe; +import kotlinx.coroutines.flow.MutableStateFlow; +import kotlinx.coroutines.flow.StateFlow; + import static java.util.Arrays.asList; import static java.util.Collections.singletonList; import static java.util.Collections.singletonMap; import static java.util.Objects.requireNonNull; +import static kotlinx.coroutines.flow.StateFlowKt.MutableStateFlow; import static net.freehaven.tor.control.TorControlCommands.HS_ADDRESS; import static net.freehaven.tor.control.TorControlCommands.HS_PRIVKEY; import static org.briarproject.mailbox.core.tor.TorConstants.CONTROL_PORT; @@ -70,7 +74,6 @@ import static org.briarproject.mailbox.core.tor.TorConstants.HS_ADDRESS_V3; import static org.briarproject.mailbox.core.tor.TorConstants.HS_PRIVATE_KEY_V3; import static org.briarproject.mailbox.core.tor.TorConstants.SETTINGS_NAMESPACE; import static org.briarproject.mailbox.core.tor.TorPlugin.State.ACTIVE; -import static org.briarproject.mailbox.core.tor.TorPlugin.State.DISABLED; import static org.briarproject.mailbox.core.tor.TorPlugin.State.ENABLING; import static org.briarproject.mailbox.core.tor.TorPlugin.State.INACTIVE; import static org.briarproject.mailbox.core.tor.TorPlugin.State.STARTING_STOPPING; @@ -155,6 +158,10 @@ public abstract class TorPlugin return new File(torDirectory, "obfs4proxy"); } + public StateFlow<State> getState() { + return state.state; + } + @Override public void startService() throws ServiceException { if (used.getAndSet(true)) throw new IllegalStateException(); @@ -404,6 +411,7 @@ public abstract class TorPlugin logException(LOG, e); } } + state.setServicePublished(); } @Nullable @@ -534,7 +542,6 @@ public abstract class TorPlugin else LOG.info("Country code: " + country); } - int reasonsDisabled = 0; boolean enableNetwork = false; boolean enableBridges = false; boolean useMeek = false; @@ -557,7 +564,6 @@ public abstract class TorPlugin LOG.info("Not using bridges"); } } - state.setReasonsDisabled(reasonsDisabled); try { if (enableNetwork) { enableBridges(enableBridges, useMeek); @@ -581,7 +587,10 @@ public abstract class TorPlugin } @ThreadSafe - protected class PluginState { + protected static class PluginState { + + private final MutableStateFlow<State> state = + MutableStateFlow(STARTING_STOPPING); @GuardedBy("this") private boolean started = false, @@ -590,10 +599,7 @@ public abstract class TorPlugin networkEnabled = false, bootstrapped = false, circuitBuilt = false, - settingsChecked = false; - - @GuardedBy("this") - private int reasonsDisabled = 0; + servicePublished = false; @GuardedBy("this") @Nullable @@ -601,7 +607,7 @@ public abstract class TorPlugin synchronized void setStarted() { started = true; -// callback.pluginStateChanged(getState()); + state.setValue(getCurrentState()); } synchronized boolean isTorRunning() { @@ -613,63 +619,52 @@ public abstract class TorPlugin stopped = true; ServerSocket ss = serverSocket; serverSocket = null; -// callback.pluginStateChanged(getState()); + state.setValue(getCurrentState()); return ss; } synchronized void setBootstrapped() { bootstrapped = true; -// callback.pluginStateChanged(getState()); + state.setValue(getCurrentState()); } synchronized boolean getAndSetCircuitBuilt() { boolean firstCircuit = !circuitBuilt; circuitBuilt = true; -// callback.pluginStateChanged(getState()); + state.setValue(getCurrentState()); return firstCircuit; } + synchronized void setServicePublished() { + servicePublished = true; + state.setValue(getCurrentState()); + } + synchronized void enableNetwork(boolean enable) { networkInitialised = true; networkEnabled = enable; if (!enable) circuitBuilt = false; -// callback.pluginStateChanged(getState()); + state.setValue(getCurrentState()); } - synchronized void setReasonsDisabled(int reasonsDisabled) { - settingsChecked = true; - this.reasonsDisabled = reasonsDisabled; -// callback.pluginStateChanged(getState()); - } - - synchronized State getState() { - if (!started || stopped || !settingsChecked) { + private synchronized State getCurrentState() { + if (!started || stopped) { return STARTING_STOPPING; } - if (reasonsDisabled != 0) return DISABLED; if (!networkInitialised) return ENABLING; if (!networkEnabled) return INACTIVE; - return bootstrapped && circuitBuilt ? ACTIVE : ENABLING; - } - - synchronized int getReasonsDisabled() { - return getState() == DISABLED ? reasonsDisabled : 0; + return bootstrapped && circuitBuilt && servicePublished ? + ACTIVE : ENABLING; } } - enum State { - + public enum State { /** * The plugin has not finished starting or has been stopped. */ STARTING_STOPPING, - /** - * The plugin is disabled by settings. - */ - DISABLED, - /** * The plugin is being enabled and can't yet make or receive * connections.