Skip to content

Commit

Permalink
Merge pull request #19 from noordstar/parser
Browse files Browse the repository at this point in the history
Expose Matrix.User
  • Loading branch information
BramvdnHeuvel authored Apr 12, 2024
2 parents 709d608 + 086e491 commit 8c73fbf
Show file tree
Hide file tree
Showing 18 changed files with 1,140 additions and 28 deletions.
5 changes: 5 additions & 0 deletions elm.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
"Internal.Config.Phantom",
"Internal.Config.Text",
"Internal.Filter.Timeline",
"Internal.Grammar.ServerName",
"Internal.Grammar.UserId",
"Internal.Tools.DecodeExtra",
"Internal.Tools.EncodeExtra",
"Internal.Tools.Hashdict",
Expand All @@ -25,16 +27,19 @@
"Internal.Values.Settings",
"Internal.Values.StateManager",
"Internal.Values.Timeline",
"Internal.Values.User",
"Internal.Values.Vault",
"Matrix",
"Matrix.Event",
"Matrix.Settings",
"Matrix.User",
"Types"
],
"elm-version": "0.19.0 <= v < 0.20.0",
"dependencies": {
"elm/core": "1.0.0 <= v < 2.0.0",
"elm/json": "1.0.0 <= v < 2.0.0",
"elm/parser": "1.0.0 <= v < 2.0.0",
"elm/time": "1.0.0 <= v < 2.0.0",
"micahhahn/elm-safe-recursion": "2.0.0 <= v < 3.0.0",
"miniBill/elm-fast-dict": "1.0.0 <= v < 2.0.0"
Expand Down
26 changes: 24 additions & 2 deletions src/Internal/Config/Text.elm
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
module Internal.Config.Text exposing
( docs, failures, fields, mappings, logs
( docs, failures, fields, mappings, logs, parses
, accessTokenFoundLocally, accessTokenExpired, accessTokenInvalid
, versionsFoundLocally, versionsReceived, versionsFailedToDecode
, unsupportedVersionForEndpoint
Expand Down Expand Up @@ -27,7 +27,7 @@ You should only do this if you know what you're doing.
## Type documentation
@docs docs, failures, fields, mappings, logs
@docs docs, failures, fields, mappings, logs, parses
## API Authentication
Expand Down Expand Up @@ -515,6 +515,28 @@ mappings =
}


{-| Logs for issues that might be found while parsing strings into meaningful data.
-}
parses :
{ historicalUserId : String -> String
, reservedIPs :
{ ipv6Toipv4 : String
, multicast : String
, futureUse : String
, unspecified : String
}
}
parses =
{ historicalUserId = \name -> "Found a historical username `" ++ name ++ "`."
, reservedIPs =
{ ipv6Toipv4 = "Detected a reserved ip address that is formerly used as an IPv6 to IPv4 relay. It is unlikely that this IP Address is real."
, multicast = "Detected a reserved ip address that is used for multicasting. It is unlikely that this IP Address is real."
, futureUse = "Detected a reserves ip address that is reserved for future use. It is unlikely that this IP Address is real if you're running a recent version of the Elm SDK."
, unspecified = "This is an unspecified ip address. It is unlikely that this IP Address is real and someone might try to break something."
}
}


{-| The Matrix homeserver can specify how it wishes to communicate, and the Elm
SDK aims to communicate accordingly. This may fail in some scenarios, however,
in which case it will throw this error.
Expand Down
5 changes: 3 additions & 2 deletions src/Internal/Filter/Timeline.elm
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ for interacting with the Matrix API.
-}

import Internal.Config.Text as Text
import Internal.Grammar.UserId as U
import Internal.Tools.Json as Json
import Json.Decode as D
import Json.Encode as E
Expand All @@ -57,7 +58,7 @@ import Set exposing (Set)
{-| Placeholder Event type so the real Event doesn't need to be imported.
-}
type alias Event a =
{ a | eventType : String, sender : String }
{ a | eventType : String, sender : U.UserID }


{-| The Timeline Filter filters events out of a timeline, guaranteeing that only
Expand Down Expand Up @@ -246,7 +247,7 @@ match (Filter f) { eventType, sender } =
let
mentionedSender : Bool
mentionedSender =
Set.member sender f.senders
Set.member (U.toString sender) f.senders

mentionedType : Bool
mentionedType =
Expand Down
279 changes: 279 additions & 0 deletions src/Internal/Grammar/ServerName.elm
Original file line number Diff line number Diff line change
@@ -0,0 +1,279 @@
module Internal.Grammar.ServerName exposing
( ServerName, toString, fromString
, serverNameParser
, HostName(..)
)

{-|
# Server name
A homeserver is uniquely identified by its server name. The server name
represents the address at which the homeserver in question can be reached by
other homeservers.
@docs ServerName, toString, fromString
## Parser
@docs serverNameParser
## Debug
@docs HostName
-}

import Internal.Tools.ParserExtra as PE
import Parser as P exposing ((|.), (|=), Parser)


{-| The hostname is the location where the server can be found.
Notice how the Matrix spec specifies that the hostname can either be a DNS name,
an IPv4Address or an IPv6Address. Since the IPv4Address is compatible with the
specification of DNS names, however, and RFC1123 (section 2.1) does not require
a client to distinguish them, we treat IPv4Addresses like DNS names.
-}
type HostName
= DNS String
| IPv6 IPv6Address


{-| The IPv6Address is represented by a list of items BEFORE and AFTER the
double colons (::).
-}
type alias IPv6Address =
{ front : List String, back : List String }


{-| The server name is a combination of a hostname and an optional port.
-}
type alias ServerName =
{ host : HostName, port_ : Maybe Int }


{-| Parser for the DNS name record. The Matrix spec bases its grammar on the
standard for internet host names, as specified by RFC1123, section 2.1, with an
extension IPv6 literals.
[RFC-1123 §2.2]
The syntax of a legal Internet host name was specified in RFC-952
[DNS:4]. One aspect of host name syntax is hereby changed: the
restriction on the first character is relaxed to allow either a
letter or a digit. Host software MUST support this more liberal
syntax.
Host software MUST handle host names of up to 63 characters and
SHOULD handle host names of up to 255 characters.
[RFC-952 §Assumptions-1]
A "name" (Net, Host, Gateway, or Domain name) is a text string up
to 24 characters drawn from the alphabet (A-Z), digits (0-9), minus
sign (-), and period (.). Note that periods are only allowed when
they serve to delimit components of "domain style names". (See
RFC-921, "Domain Name System Implementation Schedule", for
background).
-}
dnsNameParser : Parser String
dnsNameParser =
P.chompIf Char.isAlphaNum
|. P.chompWhile (\c -> Char.isAlphaNum c || c == '-' || c == '.')
|> P.getChompedString


{-| Convert a string to a server name.
-}
fromString : String -> Maybe ServerName
fromString s =
P.run (serverNameParser |. P.end) s
|> (\out ->
case out of
Ok _ ->
out

Err e ->
Debug.log "No parse" e
|> always (Debug.log "original" s)
|> always out
)
|> Result.toMaybe


{-| Parse a Hostname.
-}
hostnameParser : Parser HostName
hostnameParser =
P.oneOf
[ P.succeed IPv6
|. P.symbol "["
|= ipv6Parser
|. P.symbol "]"
, P.succeed DNS
|= dnsNameParser
]


{-| Parse all values to the left of the double colon (::)
-}
ipv6LeftParser : Parser (List String)
ipv6LeftParser =
P.oneOf
[ P.succeed []
|. P.symbol ":"
, P.succeed (|>)
|= PE.times 1 7 (ipv6NumParser |. P.symbol ":")
|= P.oneOf
[ P.succeed (\bottom tail -> tail ++ [ bottom ])
|= ipv6NumParser
, P.succeed identity
]
]


{-| Parse an ordinary IPv6 number
-}
ipv6NumParser : Parser String
ipv6NumParser =
P.chompIf Char.isHexDigit
|> P.getChompedString
|> PE.times 1 4
|> P.map String.concat


{-| Parse an IPv6 Address
-}
ipv6Parser : Parser IPv6Address
ipv6Parser =
ipv6LeftParser
|> P.andThen
(\front ->
if List.length front < 8 then
P.succeed (IPv6Address front)
|= ipv6RightParser (8 - 1 - List.length front)
-- The -1 is because :: implies one or more zeroes

else
P.succeed (IPv6Address front [])
)


{-| Parse all values to the right of the double colon (::)
-}
ipv6RightParser : Int -> Parser (List String)
ipv6RightParser n =
if n > 0 then
P.succeed identity
|. P.symbol ":"
|= P.oneOf
[ P.succeed (::)
|= ipv6NumParser
|= PE.times 0
(n - 1)
(P.succeed identity
|. P.symbol ":"
|= ipv6NumParser
)
, P.succeed []
]

else
P.succeed []
|. P.symbol ":"


{-| Convert an IPv6 address to a readable string format
-}
ipv6ToString : IPv6Address -> String
ipv6ToString { front, back } =
(if List.length front == 8 then
front

else if List.length back == 8 then
back

else
List.concat [ front, [ "" ], back ]
)
|> List.intersperse ":"
|> String.concat


portParser : Parser Int
portParser =
P.chompIf Char.isDigit
|. P.chompWhile Char.isDigit
|> P.getChompedString
|> P.andThen
(\v ->
case String.toInt v of
Just i ->
if 0 <= i && i <= 2 ^ 16 - 1 then
P.succeed i

else
P.problem ("Port out of range: " ++ v)

Nothing ->
P.problem "Not a port number"
)


{-| Parse a server name. Generally used by other identifiers that have a server
name as one of its parts.
-}
serverNameParser : Parser ServerName
serverNameParser =
P.succeed ServerName
|= hostnameParser
|= P.oneOf
[ P.succeed Just
|. P.symbol ":"
|= portParser
, P.succeed Nothing
]


{-| Convert a parsed server name back to a string.
-}
toString : ServerName -> String
toString { host, port_ } =
let
hostString : String
hostString =
case host of
DNS name ->
name

IPv6 { front, back } ->
(if List.length front == 8 then
List.intersperse ":" front

else if List.length back == 8 then
List.intersperse ":" back

else
List.concat
[ List.intersperse ":" front
, [ "::" ]
, List.intersperse ":" back
]
)
|> String.concat
|> (\i -> "[" ++ i ++ "]")

portString : String
portString =
port_
|> Maybe.map String.fromInt
|> Maybe.map ((++) ":")
|> Maybe.withDefault ""
in
hostString ++ portString
Loading

0 comments on commit 8c73fbf

Please sign in to comment.