packages feed

git-annex-10.20260115: Assistant/WebApp/Configurators/Pairing.hs

{- git-annex assistant webapp configurator for pairing
 -
 - Copyright 2012,2016 Joey Hess <id@joeyh.name>
 -
 - Licensed under the GNU AGPL version 3 or higher.
 -}

{-# LANGUAGE TypeFamilies, QuasiQuotes, TemplateHaskell, OverloadedStrings, FlexibleContexts #-}

module Assistant.WebApp.Configurators.Pairing where

import Assistant.WebApp.Common
import Utility.Tor
import Utility.Su
import Assistant.WebApp.Pairing
import qualified Utility.MagicWormhole as Wormhole
import Assistant.MakeRemote
import Assistant.RemoteControl
import Assistant.Sync
import Assistant.WebApp.SideBar
import Command.P2P (unusedPeerRemoteName, PairingResult(..))
import P2P.Address
import Annex.Path
import Utility.Process.Transcript

import qualified Data.Map as M
import qualified Data.Text as T
import Control.Concurrent.STM hiding (check)

getStartWormholePairFriendR :: Handler Html
getStartWormholePairFriendR = startWormholePairR PairingWithFriend

getStartWormholePairSelfR :: Handler Html
getStartWormholePairSelfR = startWormholePairR PairingWithSelf

startWormholePairR :: PairingWith -> Handler Html
startWormholePairR pairingwith = whenTorInstalled $ whenWormholeInstalled $
	pairPage $ do
		sucommand <- liftIO $ mkSuCommand "git-annex" [Param "enable-tor"]
		$(widgetFile "configurators/pairing/wormhole/start")

getPrepareWormholePairR :: PairingWith -> Handler Html
getPrepareWormholePairR pairingwith = do
	enableTor
	myaddrs <- liftAnnex loadP2PAddresses
	remotename <- liftAnnex unusedPeerRemoteName
	h <- liftAssistant $
		startWormholePairing pairingwith remotename myaddrs
	i <- liftIO . addWormholePairingState h
		=<< wormholePairingState <$> getYesod
	redirect $ RunningWormholePairR i

enableTor :: Handler ()
enableTor = do
	gitannex <- fromOsPath <$> liftIO programPath
	(transcript, ok) <- liftIO $ processTranscript gitannex ["enable-tor"] Nothing
	if ok
		-- Reload remotedameon so it's serving the tor hidden
		-- service.
		then liftAssistant $ sendRemoteControl RELOAD
		else giveup $ "Failed to enable tor\n\n" ++ transcript

getRunningWormholePairR :: WormholePairingId -> Handler Html
getRunningWormholePairR = runningWormholePairR

postRunningWormholePairR :: WormholePairingId -> Handler Html
postRunningWormholePairR = runningWormholePairR

runningWormholePairR :: WormholePairingId -> Handler Html
runningWormholePairR i = go =<< getWormholePairingHandle i
  where
	go Nothing = redirect StartWormholePairFriendR
	go (Just h) = pairPage $  withPairingWith h $ \pairingwith -> do
		ourcode <- liftIO $ getOurWormholeCode h
		let codeprompt = case pairingwith of
			PairingWithFriend -> "Your friend's pairing code"
			PairingWithSelf -> "The other device's pairing code"
		((result, form), enctype) <- liftH $
			runFormPostNoToken $ renderBootstrap3 bootstrapFormLayout $
				areq (checkwormholecode ourcode pairingwith textField) (bfs codeprompt) Nothing
		case result of
			FormSuccess t -> case Wormhole.toCode (T.unpack t) of
				Nothing -> giveup invalidcode
				Just theircode -> finish h theircode
			_ -> showform form enctype ourcode pairingwith
	
	showform form enctype ourcode pairingwith =
		$(widgetFile "configurators/pairing/wormhole/prompt")
	
	checkwormholecode ourcode pairingwith = check $ \t ->
		case Wormhole.toCode (T.unpack t) of
			Nothing -> Left (T.pack invalidcode)
			Just theircode
				| theircode == ourcode -> Left $
					case pairingwith of
						PairingWithSelf -> "Oops -- You entered this repository's pairing code. Enter the pairing code of the *other* repository."
						PairingWithFriend -> "Oops -- You entered your pairing code. Enter your friend's pairing code."
				| otherwise -> Right t

	invalidcode = "That does not look like a valid pairing code. Try again..."

	finish h theircode = do
		void $ liftIO $ sendTheirWormholeCode h theircode
		res <- liftAssistant $ finishWormholePairing h
		case res of
			SendFailed -> giveup "Failed sending data to pair."
			ReceiveFailed -> giveup "Failed receiving data from pair."
			LinkFailed e -> giveup $ "Failed linking to pair: " ++ e
			PairSuccess -> withRemoteName h $ \remotename -> do
				r <- liftAnnex $ addRemote (return remotename)
				liftAssistant $ syncRemote r
				liftAssistant $ sendRemoteControl RELOAD
				redirect DashboardR

getWormholePairingHandle :: WormholePairingId -> Handler (Maybe WormholePairingHandle)
getWormholePairingHandle i = do
	s <- wormholePairingState <$> getYesod
	liftIO $ atomically $ M.lookup i <$> readTVar s

whenTorInstalled :: Handler Html -> Handler Html
whenTorInstalled a = ifM (liftIO torIsInstalled)
	( a
	, page "Need Tor" (Just Configuration) $
		$(widgetFile "configurators/needtor")
	)

whenWormholeInstalled :: Handler Html -> Handler Html
whenWormholeInstalled a = ifM (liftIO Wormhole.isInstalled)
	( a
	, page "Need Magic Wormhole" (Just Configuration) $
		$(widgetFile "configurators/needmagicwormhole")
	)

pairPage :: Widget -> Handler Html
pairPage = page "Pairing" (Just Configuration)