@@ -109,7 +109,7 @@ module Simplex.Messaging.Agent.Protocol
109109 CRClientData ,
110110 ServiceScheme ,
111111 FixedLinkData (.. ),
112- UserLinkData (.. ),
112+ ConnLinkData (.. ),
113113 OwnerAuth (.. ),
114114 OwnerId ,
115115 ConnectionLink (.. ),
@@ -156,6 +156,7 @@ module Simplex.Messaging.Agent.Protocol
156156 updateSMPServerHosts ,
157157 shortenShortLink ,
158158 restoreShortLink ,
159+ linkUserData ,
159160 )
160161where
161162
@@ -167,7 +168,7 @@ import qualified Data.Attoparsec.ByteString.Char8 as A
167168import qualified Data.ByteString.Base64.URL as B64
168169import Data.ByteString.Char8 (ByteString )
169170import qualified Data.ByteString.Char8 as B
170- import Data.Char (isDigit )
171+ import Data.Char (isDigit , toLower , toUpper )
171172import Data.Foldable (find )
172173import Data.Functor (($>) )
173174import Data.Int (Int64 )
@@ -1349,7 +1350,7 @@ deriving instance Show AConnectionRequestUri
13491350
13501351data ConnShortLink (m :: ConnectionMode ) where
13511352 CSLInvitation :: SMPServer -> SMP. LinkId -> LinkKey -> ConnShortLink 'CMInvitation
1352- CSLContact :: SMPServer -> ContactConnType -> LinkKey -> ConnShortLink 'CMContact
1353+ CSLContact :: ContactConnType -> SMPServer -> LinkKey -> ConnShortLink 'CMContact
13531354
13541355deriving instance Eq (ConnShortLink m )
13551356
@@ -1369,7 +1370,7 @@ instance ConnectionModeI c => ToField (ConnShortLink c) where toField = toField
13691370
13701371instance (Typeable c , ConnectionModeI c ) => FromField (ConnShortLink c ) where fromField = blobFieldDecoder strDecode
13711372
1372- data ContactConnType = CCTContact | CCTGroup deriving (Eq , Show )
1373+ data ContactConnType = CCTContact | CCTChannel | CCTGroup deriving (Eq , Show )
13731374
13741375data AConnShortLink = forall m . ConnectionModeI m => ACSL (SConnectionMode m ) (ConnShortLink m )
13751376
@@ -1406,50 +1407,84 @@ instance ConnectionModeI m => FromJSON (ConnectionLink m) where
14061407
14071408instance ConnectionModeI m => StrEncoding (ConnShortLink m ) where
14081409 strEncode = \ case
1409- CSLInvitation srv (SMP. EntityId lnkId) (LinkKey k) -> encLink srv (lnkId <> k) " i "
1410- CSLContact srv ct (LinkKey k) -> encLink srv k $ case ct of CCTContact -> " c " ; CCTGroup -> " g "
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
14111412 where
14121413 encLink (SMPServer (h :| hs) port (C. KeyHash kh)) linkUri linkType =
1413- " https://" <> strEncode h <> port' <> " /" <> linkType <> " #" <> khStr <> hosts <> B64. encodeUnpadded linkUri
1414+ B. concat [ " https://" , strEncode h, port', " /" , B. singleton linkType, " #" , khStr, hosts, B64. encodeUnpadded linkUri]
14141415 where
14151416 port' = if null port then " " else B. pack (' :' : port)
14161417 hosts = if null hs then " " else strEncode (TransportHosts_ hs) <> " /"
14171418 khStr = if B. null kh then " " else B64. encodeUnpadded kh <> " @"
1418- strP = do
1419- ACSL m l <- strP
1420- case testEquality m $ sConnectionMode @ m of
1421- Just Refl -> pure l
1422- _ -> fail " bad short link mode"
1419+ strP = (\ (ACSL _ l) -> checkConnMode l) <$?> strP
1420+ {-# INLINE strP #-}
14231421
14241422instance StrEncoding AConnShortLink where
14251423 strEncode (ACSL _ l) = strEncode l
1424+ {-# INLINE strEncode #-}
14261425 strP = do
14271426 h <- " https://" *> strP
14281427 port <- A. char ' :' *> (B. unpack <$> A. takeWhile1 isDigit) <|> pure " "
1429- linkType <- A. char ' /' *> A. anyChar
1428+ contactType <- A. char ' /' *> contactTypeP
14301429 keyHash <- optional (A. char ' /' ) *> A. char ' #' *> (strP <* A. char ' @' <|> pure (C. KeyHash " " ))
14311430 TransportHosts_ hs <- strP <* " /" <|> pure (TransportHosts_ [] )
14321431 linkUri <- strP
14331432 let srv = SMPServer (h :| hs) port keyHash
1434- case linkType of
1435- ' i '
1433+ case contactType of
1434+ Nothing
14361435 | B. length linkUri == 56 ->
14371436 let (lnkId, k) = B. splitAt 24 linkUri
14381437 in pure $ ACSL SCMInvitation $ CSLInvitation srv (SMP. EntityId lnkId) (LinkKey k)
14391438 | otherwise -> fail " bad ConnShortLink: incorrect linkID and key length"
1440- ' c ' -> contactP srv CCTContact linkUri
1441- ' g ' -> contactP srv CCTGroup linkUri
1442- _ -> fail " bad ConnShortLink: unknown link type "
1439+ Just ct
1440+ | B. length linkUri == 32 -> pure $ ACSL SCMContact $ CSLContact ct srv ( LinkKey linkUri)
1441+ | otherwise -> fail " bad ConnShortLink: incorrect key length "
14431442 where
1444- contactP srv ct k
1445- | B. length k == 32 = pure $ ACSL SCMContact $ CSLContact srv ct (LinkKey k)
1446- | otherwise = fail " bad ConnShortLink: incorrect key length"
1443+ contactTypeP = do
1444+ Just <$> (A. anyChar >>= ctTypeP . toUpper)
1445+ <|> A. char ' i' $> Nothing
1446+ <|> fail " unknown link type"
1447+
1448+ instance ConnectionModeI m => Encoding (ConnShortLink m ) where
1449+ smpEncode = \ case
1450+ CSLInvitation srv lnkId (LinkKey k) -> smpEncode (CMInvitation , srv, lnkId, k)
1451+ CSLContact ct srv (LinkKey k) -> smpEncode (CMContact , ctTypeChar ct, srv, k)
1452+ smpP = (\ (ACSL _ l) -> checkConnMode l) <$?> smpP
1453+ {-# INLINE smpP #-}
1454+
1455+ instance Encoding AConnShortLink where
1456+ smpEncode (ACSL _ l) = smpEncode l
1457+ {-# INLINE smpEncode #-}
1458+ smpP =
1459+ smpP >>= \ case
1460+ CMInvitation -> do
1461+ (srv, lnkId, k) <- smpP
1462+ pure $ ACSL SCMInvitation $ CSLInvitation srv lnkId (LinkKey k)
1463+ CMContact -> do
1464+ ct <- ctTypeP =<< A. anyChar
1465+ (srv, k) <- smpP
1466+ pure $ ACSL SCMContact $ CSLContact ct srv (LinkKey k)
1467+
1468+ ctTypeP :: Char -> Parser ContactConnType
1469+ ctTypeP = \ case
1470+ ' A' -> pure CCTContact
1471+ ' C' -> pure CCTChannel
1472+ ' G' -> pure CCTGroup
1473+ _ -> fail " unknown contact address type"
1474+ {-# INLINE ctTypeP #-}
1475+
1476+ ctTypeChar :: ContactConnType -> Char
1477+ ctTypeChar = \ case
1478+ CCTContact -> ' A'
1479+ CCTChannel -> ' C'
1480+ CCTGroup -> ' G'
1481+ {-# INLINE ctTypeChar #-}
14471482
14481483-- the servers passed to this function should be all preset servers, not servers configured by the user.
14491484shortenShortLink :: NonEmpty SMPServer -> ConnShortLink m -> ConnShortLink m
14501485shortenShortLink presetSrvs = \ case
14511486 CSLInvitation srv lnkId linkKey -> CSLInvitation (shortServer srv) lnkId linkKey
1452- CSLContact srv ct linkKey -> CSLContact (shortServer srv) ct linkKey
1487+ CSLContact ct srv linkKey -> CSLContact ct (shortServer srv) linkKey
14531488 where
14541489 shortServer srv@ (SMPServer hs@ (h :| _) p kh) =
14551490 if isPresetServer then SMPServer [h] " " (C. KeyHash " " ) else srv
@@ -1465,7 +1500,7 @@ shortenShortLink presetSrvs = \case
14651500restoreShortLink :: NonEmpty SMPServer -> ConnShortLink m -> ConnShortLink m
14661501restoreShortLink presetSrvs = \ case
14671502 CSLInvitation srv lnkId linkKey -> CSLInvitation (fullServer srv) lnkId linkKey
1468- CSLContact srv ct linkKey -> CSLContact (fullServer srv) ct linkKey
1503+ CSLContact ct srv linkKey -> CSLContact ct (fullServer srv) linkKey
14691504 where
14701505 fullServer = \ case
14711506 s@ (SMPServer [_] " " (C. KeyHash " " )) -> fromMaybe s $ findPresetServer s presetSrvs
@@ -1500,14 +1535,28 @@ type CRClientData = Text
15001535data FixedLinkData c = FixedLinkData
15011536 { agentVRange :: VersionRangeSMPA ,
15021537 rootKey :: C. PublicKeyEd25519 ,
1503- connReq :: Maybe ( ConnectionRequestUri c )
1538+ connReq :: ConnectionRequestUri c
15041539 }
15051540
1506- data UserLinkData = UserLinkData
1507- { agentVRange :: VersionRangeSMPA ,
1508- owners :: [OwnerAuth ],
1509- userData :: ConnInfo
1510- }
1541+ data ConnLinkData c where
1542+ InvitationLinkData :: VersionRangeSMPA -> ConnInfo -> ConnLinkData 'CMInvitation
1543+ ContactLinkData ::
1544+ { agentVRange :: VersionRangeSMPA ,
1545+ -- direct connection via connReq in fixed data is allowed.
1546+ direct :: Bool ,
1547+ -- additional owner keys to sign changes of mutable data.
1548+ owners :: [OwnerAuth ],
1549+ -- alternative addresses of chat relays that receive requests for this contact address.
1550+ relays :: [ConnShortLink 'CMContact],
1551+ userData :: ConnInfo
1552+ } -> ConnLinkData 'CMContact
1553+
1554+ data AConnLinkData = forall m . ConnectionModeI m => ACLD (SConnectionMode m ) (ConnLinkData m )
1555+
1556+ linkUserData :: ConnLinkData c -> ConnInfo
1557+ linkUserData = \ case
1558+ InvitationLinkData _ d -> d
1559+ ContactLinkData {userData} -> userData
15111560
15121561type OwnerId = ByteString
15131562
@@ -1540,14 +1589,28 @@ instance ConnectionModeI c => Encoding (FixedLinkData c) where
15401589 (agentVRange, rootKey, connReq) <- smpP
15411590 pure FixedLinkData {agentVRange, rootKey, connReq}
15421591
1543- instance Encoding UserLinkData where
1544- smpEncode UserLinkData {agentVRange, owners, userData} =
1545- smpEncode agentVRange <> smpEncodeList owners <> smpEncode (Large userData)
1546- smpP = do
1547- agentVRange <- smpP
1548- owners <- smpListP
1549- Large userData <- smpP
1550- pure UserLinkData {agentVRange, owners, userData}
1592+ instance ConnectionModeI c => Encoding (ConnLinkData c ) where
1593+ smpEncode = \ case
1594+ InvitationLinkData vr userData -> smpEncode (CMInvitation , vr, userData)
1595+ ContactLinkData {agentVRange, direct, owners, relays, userData} ->
1596+ B. concat [smpEncode (CMContact , agentVRange, direct), smpEncodeList owners, smpEncodeList relays, smpEncode userData]
1597+ smpP = (\ (ACLD _ d) -> checkConnMode d) <$?> smpP
1598+ {-# INLINE smpP #-}
1599+
1600+ instance Encoding AConnLinkData where
1601+ smpEncode (ACLD _ d) = smpEncode d
1602+ {-# INLINE smpEncode #-}
1603+ smpP =
1604+ smpP >>= \ case
1605+ CMInvitation -> do
1606+ (vr, userData) <- smpP
1607+ pure $ ACLD SCMInvitation $ InvitationLinkData vr userData
1608+ CMContact -> do
1609+ (agentVRange, direct) <- smpP
1610+ owners <- smpListP
1611+ relays <- smpListP
1612+ userData <- smpP
1613+ pure $ ACLD SCMContact ContactLinkData {agentVRange, direct, owners, relays, userData}
15511614
15521615-- | SMP queue status.
15531616data QueueStatus
0 commit comments