@@ -118,10 +118,13 @@ module Simplex.Messaging.Agent.Protocol
118118 AConnShortLink (.. ),
119119 CreatedConnLink (.. ),
120120 ContactConnType (.. ),
121+ ShortLinkScheme (.. ),
121122 LinkKey (.. ),
122123 sameConnReqContact ,
123124 simplexChat ,
124125 connReqUriP' ,
126+ simplexConnReqUri ,
127+ simplexShortLink ,
125128 AgentErrorType (.. ),
126129 CommandErrorType (.. ),
127130 ConnectionErrorType (.. ),
@@ -168,7 +171,7 @@ import qualified Data.Attoparsec.ByteString.Char8 as A
168171import qualified Data.ByteString.Base64.URL as B64
169172import Data.ByteString.Char8 (ByteString )
170173import qualified Data.ByteString.Char8 as B
171- import Data.Char (isDigit , toLower , toUpper )
174+ import Data.Char (toLower , toUpper )
172175import Data.Foldable (find )
173176import Data.Functor (($>) )
174177import Data.Int (Int64 )
@@ -1335,6 +1338,11 @@ data ConnectionRequestUri (m :: ConnectionMode) where
13351338 -- they are passed in AgentInvitation message
13361339 CRContactUri :: ConnReqUriData -> ConnectionRequestUri CMContact
13371340
1341+ simplexConnReqUri :: ConnectionRequestUri m -> ConnectionRequestUri m
1342+ simplexConnReqUri = \ case
1343+ CRInvitationUri crData e2eParams -> CRInvitationUri crData {crScheme = SSSimplex } e2eParams
1344+ CRContactUri crData -> CRContactUri crData {crScheme = SSSimplex }
1345+
13381346deriving instance Eq (ConnectionRequestUri m )
13391347
13401348deriving instance Show (ConnectionRequestUri m )
@@ -1348,14 +1356,21 @@ instance Eq AConnectionRequestUri where
13481356
13491357deriving instance Show AConnectionRequestUri
13501358
1359+ data ShortLinkScheme = SLSSimplex | SLSServer deriving (Eq , Show )
1360+
13511361data ConnShortLink (m :: ConnectionMode ) where
1352- CSLInvitation :: SMPServer -> SMP. LinkId -> LinkKey -> ConnShortLink 'CMInvitation
1353- CSLContact :: ContactConnType -> SMPServer -> LinkKey -> ConnShortLink 'CMContact
1362+ CSLInvitation :: ShortLinkScheme -> SMPServer -> SMP. LinkId -> LinkKey -> ConnShortLink 'CMInvitation
1363+ CSLContact :: ShortLinkScheme -> ContactConnType -> SMPServer -> LinkKey -> ConnShortLink 'CMContact
13541364
13551365deriving instance Eq (ConnShortLink m )
13561366
13571367deriving instance Show (ConnShortLink m )
13581368
1369+ simplexShortLink :: ConnShortLink m -> ConnShortLink m
1370+ simplexShortLink = \ case
1371+ CSLInvitation _ srv lnkId k -> CSLInvitation SLSSimplex srv lnkId k
1372+ CSLContact _ ct srv k -> CSLContact SLSSimplex ct srv k
1373+
13591374newtype LinkKey = LinkKey ByteString -- sha3-256(fixed_data)
13601375 deriving (Eq , Show )
13611376 deriving newtype (FromField , StrEncoding )
@@ -1407,47 +1422,64 @@ instance ConnectionModeI m => FromJSON (ConnectionLink m) where
14071422
14081423instance ConnectionModeI m => StrEncoding (ConnShortLink m ) where
14091424 strEncode = \ case
1410- CSLInvitation srv (SMP. EntityId lnkId) (LinkKey k) -> encLink srv lnkId k ' i'
1411- CSLContact ct srv (LinkKey k) -> encLink srv " " k $ toLower $ ctTypeChar ct
1425+ CSLInvitation sch srv (SMP. EntityId lnkId) (LinkKey k) -> slEncode sch srv ' i' lnkId k
1426+ CSLContact sch ct srv (LinkKey k) -> slEncode sch srv ( toLower $ ctTypeChar ct) " " k
14121427 where
1413- encLink (SMPServer (h :| hs) port (C. KeyHash kh)) lnkId k linkType =
1414- B. concat [" https:// " , strEncode h, port', " /" , B. singleton linkType, " #" , khStr, hosts, lnkIdStr, B64. encodeUnpadded k]
1428+ slEncode sch (SMPServer (h :| hs) port (C. KeyHash kh)) linkType lnkId k =
1429+ B. concat [authority, " /" , B. singleton linkType, " #" , lnkIdStr, B64. encodeUnpadded k, queryStr ]
14151430 where
1416- port' = if null port then " " else B. pack (' :' : port)
1417- khStr = if B. null kh then " " else B64. encodeUnpadded kh <> " &"
1418- hosts = if null hs then " " else strEncode (TransportHosts_ hs) <> " /"
1419- lnkIdStr = if B. null lnkId then " " else B64. encodeUnpadded lnkId <> " ."
1431+ (authority, paramHosts) = case sch of
1432+ SLSSimplex -> (" simplex:" , h : hs)
1433+ SLSServer -> (" https://" <> strEncode h, hs)
1434+ lnkIdStr = if B. null lnkId then " " else B64. encodeUnpadded lnkId <> " /"
1435+ queryStr = if B. null query then " " else " ?" <> query
1436+ query =
1437+ strEncode . QSP QEscape $
1438+ [(" h" , strEncode (TransportHosts_ paramHosts)) | not (null paramHosts)]
1439+ <> [(" p" , B. pack port) | not (null port)]
1440+ <> [(" c" , B64. encodeUnpadded kh) | not (B. null kh)]
14201441 strP = (\ (ACSL _ l) -> checkConnMode l) <$?> strP
14211442 {-# INLINE strP #-}
14221443
14231444instance StrEncoding AConnShortLink where
14241445 strEncode (ACSL _ l) = strEncode l
14251446 {-# INLINE strEncode #-}
14261447 strP = do
1427- h <- " https://" *> strP
1428- port <- A. char ' :' *> (B. unpack <$> A. takeWhile1 isDigit) <|> pure " "
1429- contactType <- A. char ' /' *> contactTypeP
1430- keyHash <- optional (A. char ' /' ) *> A. char ' #' *> (strP <* A. char ' &' <|> pure (C. KeyHash " " ))
1431- TransportHosts_ hs <- strP <* " /" <|> pure (TransportHosts_ [] )
1432- let srv = SMPServer (h :| hs) port keyHash
1433- case contactType of
1448+ (sch, h_) <- authorityP <* A. char ' /'
1449+ ct_ <- contactTypeP <* optional (A. char ' /' ) <* A. char ' #'
1450+ case ct_ of
14341451 Nothing -> do
1435- lnkId <- strP <* A. char ' . '
1452+ lnkId <- strP <* A. char ' / '
14361453 k <- strP
1437- pure $ ACSL SCMInvitation $ CSLInvitation srv (SMP. EntityId lnkId) (LinkKey k)
1454+ srv <- serverQueryP h_
1455+ pure $ ACSL SCMInvitation $ CSLInvitation sch srv (SMP. EntityId lnkId) (LinkKey k)
14381456 Just ct -> do
14391457 k <- strP
1440- pure $ ACSL SCMContact $ CSLContact ct srv (LinkKey k)
1458+ srv <- serverQueryP h_
1459+ pure $ ACSL SCMContact $ CSLContact sch ct srv (LinkKey k)
14411460 where
1461+ authorityP =
1462+ " simplex:" $> (SLSSimplex , Nothing )
1463+ <|> " https://" *> ((SLSServer ,) . Just <$> strP)
1464+ <|> fail " bad short link scheme"
14421465 contactTypeP = do
14431466 Just <$> (A. anyChar >>= ctTypeP . toUpper)
14441467 <|> A. char ' i' $> Nothing
1445- <|> fail " unknown link type"
1468+ <|> fail " unknown short link type"
1469+ serverQueryP h_ =
1470+ optional (A. char ' ?' *> strP) >>= \ case
1471+ Nothing -> maybe noServer (pure . SMPServerOnlyHost ) h_
1472+ Just query -> do
1473+ hs <- maybe noServer pure . L. nonEmpty . maybe id (:) h_ . maybe [] thList_ =<< queryParam_ " h" query
1474+ p <- maybe " " show <$> queryParam_ @ Word16 " p" query
1475+ kh <- fromMaybe (C. KeyHash " " ) <$> queryParam_ " c" query
1476+ pure $ SMPServer hs p kh
1477+ noServer = fail " short link without server"
14461478
14471479instance ConnectionModeI m => Encoding (ConnShortLink m ) where
14481480 smpEncode = \ case
1449- CSLInvitation srv lnkId (LinkKey k) -> smpEncode (CMInvitation , srv, lnkId, k)
1450- CSLContact ct srv (LinkKey k) -> smpEncode (CMContact , ctTypeChar ct, srv, k)
1481+ CSLInvitation _ srv lnkId (LinkKey k) -> smpEncode (CMInvitation , srv, lnkId, k)
1482+ CSLContact _ ct srv (LinkKey k) -> smpEncode (CMContact , ctTypeChar ct, srv, k)
14511483 smpP = (\ (ACSL _ l) -> checkConnMode l) <$?> smpP
14521484 {-# INLINE smpP #-}
14531485
@@ -1458,11 +1490,11 @@ instance Encoding AConnShortLink where
14581490 smpP >>= \ case
14591491 CMInvitation -> do
14601492 (srv, lnkId, k) <- smpP
1461- pure $ ACSL SCMInvitation $ CSLInvitation srv lnkId (LinkKey k)
1493+ pure $ ACSL SCMInvitation $ CSLInvitation SLSServer srv lnkId (LinkKey k)
14621494 CMContact -> do
14631495 ct <- ctTypeP =<< A. anyChar
14641496 (srv, k) <- smpP
1465- pure $ ACSL SCMContact $ CSLContact ct srv (LinkKey k)
1497+ pure $ ACSL SCMContact $ CSLContact SLSServer ct srv (LinkKey k)
14661498
14671499ctTypeP :: Char -> Parser ContactConnType
14681500ctTypeP = \ case
@@ -1482,11 +1514,11 @@ ctTypeChar = \case
14821514-- the servers passed to this function should be all preset servers, not servers configured by the user.
14831515shortenShortLink :: NonEmpty SMPServer -> ConnShortLink m -> ConnShortLink m
14841516shortenShortLink presetSrvs = \ case
1485- CSLInvitation srv lnkId linkKey -> CSLInvitation (shortServer srv) lnkId linkKey
1486- CSLContact ct srv linkKey -> CSLContact ct (shortServer srv) linkKey
1517+ CSLInvitation sch srv lnkId linkKey -> CSLInvitation sch (shortServer srv) lnkId linkKey
1518+ CSLContact sch ct srv linkKey -> CSLContact sch ct (shortServer srv) linkKey
14871519 where
14881520 shortServer srv@ (SMPServer hs@ (h :| _) p kh) =
1489- if isPresetServer then SMPServer [h] " " ( C. KeyHash " " ) else srv
1521+ if isPresetServer then SMPServerOnlyHost h else srv
14901522 where
14911523 isPresetServer = case findPresetServer srv presetSrvs of
14921524 Just (SMPServer hs' p' kh') ->
@@ -1495,14 +1527,17 @@ shortenShortLink presetSrvs = \case
14951527 && kh == kh'
14961528 Nothing -> False
14971529
1530+ pattern SMPServerOnlyHost :: TransportHost -> SMPServer
1531+ pattern SMPServerOnlyHost h = SMPServer [h] " " (C. KeyHash " " )
1532+
14981533-- the servers passed to this function should be all preset servers, not servers configured by the user.
14991534restoreShortLink :: NonEmpty SMPServer -> ConnShortLink m -> ConnShortLink m
15001535restoreShortLink presetSrvs = \ case
1501- CSLInvitation srv lnkId linkKey -> CSLInvitation (fullServer srv) lnkId linkKey
1502- CSLContact ct srv linkKey -> CSLContact ct (fullServer srv) linkKey
1536+ CSLInvitation sch srv lnkId linkKey -> CSLInvitation sch (fullServer srv) lnkId linkKey
1537+ CSLContact sch ct srv linkKey -> CSLContact sch ct (fullServer srv) linkKey
15031538 where
15041539 fullServer = \ case
1505- s@ (SMPServer [_] " " ( C. KeyHash " " ) ) -> fromMaybe s $ findPresetServer s presetSrvs
1540+ s@ (SMPServerOnlyHost _ ) -> fromMaybe s $ findPresetServer s presetSrvs
15061541 s -> s
15071542
15081543findPresetServer :: SMPServer -> NonEmpty SMPServer -> Maybe SMPServer
0 commit comments