]> gitweb.michael.orlitzky.com - email-validator.git/commitdiff
email-validator.cabal: bump to version 1.1.0 master 1.1.0
authorMichael Orlitzky <michael@orlitzky.com>
Thu, 25 Apr 2024 00:36:54 +0000 (20:36 -0400)
committerMichael Orlitzky <michael@orlitzky.com>
Thu, 25 Apr 2024 00:36:54 +0000 (20:36 -0400)
doc/COPYING [new file with mode: 0644]
doc/man1/email-validator.1
email-validator.cabal
makefile
src/Main.hs

diff --git a/doc/COPYING b/doc/COPYING
new file mode 100644 (file)
index 0000000..dcca366
--- /dev/null
@@ -0,0 +1,15 @@
+email-validator: basic syntax and deliverability checks on email addresses
+Copyright (C) 2024  Michael Orlitzky
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with this program.  If not, see <https://www.gnu.org/licenses/>.
index 1e0e144940db48e78f3b7ddc5428457c81d7b86b..605b57010256b19858024cf973232358b757a6f3 100644 (file)
@@ -33,10 +33,12 @@ grammar (see the \fB\-\-rfc5322\fR option).
 
 .IP \[bu]
 Confirmation of the existence of an \fIMX\fR record for the domain
-part of the address. This is not required; in fact many domains accept
-mail via an \fIA\fR record for e.g. example.com which is used in lieu
-of an \fIMX\fR record. This behavior can be controlled via the
-\fR\-\-accept\-a\fR flag.
+part of the address. NULLMX (RFC7505) records are not accepted. This
+is not required; in fact many domains accept mail via an \fIA\fR
+record for (say) example.com which is used in lieu of an \fIMX\fR
+record. This behavior can be controlled via the \fR\-\-accept\-a\fR
+flag, but note that \fR\-\-accept\-a\fR is ignored for domains that
+have NULLMX records.
 
 .P
 These checks are performed in parallel using the number of available
index c899d8c044253d5ef856bf519281a5fa5dbf9500..94c14f48ce3f72683f4fa0f90561df8ae496913c 100644 (file)
@@ -1,15 +1,16 @@
 cabal-version:  3.0
 name:           email-validator
-version:        1.0.0
+version:        1.1.0
 author:         Michael Orlitzky
 maintainer:    Michael Orlitzky <michael@orlitzky.com>
-homepage:       http://michael.orlitzky.com/code/email-validator.xhtml
+homepage:       https://michael.orlitzky.com/code/email-validator.xhtml
 bug-reports:    mailto:michael@orlitzky.com
 category:       Utils
-license:        AGPL-3.0-only
+license:        AGPL-3.0-or-later
 license-file:   doc/LICENSE
 build-type:     Simple
 extra-source-files:
+  doc/COPYING
   doc/man1/email-validator.1
 synopsis:
   Perform basic syntax and deliverability checks on email addresses.
@@ -18,33 +19,13 @@ description:
 
     * Ensuring that the length of local and domain parts is within the
       RFC-specified limits.
-
     * A syntax check using a regular expression, or the full RFC 5322
-      grammar (see the @--rfc5322@ option).
-
-    * Confirmation of the existence of an @MX@ record for the domain part of
-      the address. This is not required; in fact many domains accept mail
-      via an @A@ record for e.g. example.com which is used in lieu of an @MX@
-      record. This behavior can be controlled via the @--accept-a@ flag.
-
-  These checks are performed in parallel using the number of available
-  threads. To increase the number of threads, you can pass the
-  appropriate flag to the GHC runtime.
-
-  This will set the number of threads to 25:
-
-  @
-  $ email-validator +RTS -N25 < addresses.csv
-  @
-
-  /Input/
-
-  The @input@ (via stdin) should be a list of email addresses,
-  one per line. Empty lines will be ignored.
-
-  /Output/
+      grammar.
+    * Confirmation of valid @MX@ records (or, optionally, @A@
+      records) for the domain.
 
-  Valid email addresses will be written to stdout, one per line.
+  A complete description, options, and examples can be found in the
+  man page.
 
 
 executable email-validator
@@ -110,5 +91,5 @@ test-suite doctests
 
 source-repository head
   type: git
-  location: http://gitweb.michael.orlitzky.com/email-validator.git
+  location: https://gitweb.michael.orlitzky.com/email-validator.git
   branch: master
index e77b22752ece367de4cd88c66533e3d7c234be22..5052789b5c401884f51d5e0dd4c69a039eed869e 100644 (file)
--- a/makefile
+++ b/makefile
@@ -13,6 +13,7 @@ HCFLAGS += -Weverything \
            -Wno-prepositive-qualified-module \
            -Wno-missing-safe-haskell-mode \
            -Wno-missing-deriving-strategies \
+           -Wno-missing-kind-signatures \
            -rtsopts \
            -threaded
 
index 1cc4c73aa47d963e56f30a7188f05781f010b76c..ab5f93ac4cdf8a40125ac34ba6b014e91bc60df1 100644 (file)
@@ -41,28 +41,63 @@ import EmailAddress(
 resolv_conf ::  ResolvConf
 resolv_conf = defaultResolvConf { resolvTimeout = 10 * 1000 * 1000 }
 
--- | A list of common domains, there's no need to waste MX lookups
---   on these.
+-- | A list of common domains, there's no need to waste MX lookups on
+--   these. This is a very limited list; I don't want to be in the
+--   business of monitoring a million domains for MX record updates.
 common_domains :: [Domain]
 common_domains = map BS.pack [ "aol.com",
                                "comcast.net",
+                               "cox.net",
                                "gmail.com",
+                               "gmx.de",
+                               "googlemail.com",
+                               "hotmail.com",
+                               "icloud.com",
+                               "live.com",
+                               "me.com",
                                "msn.com",
+                               "outlook.com",
+                               "proton.me",
+                               "protonmail.ch",
+                               "protonmail.com",
                                "yahoo.com",
                                "verizon.net" ]
 
 
 -- | Check whether the given domain has a valid MX record.
-validate_mx :: Resolver -> Domain -> IO Bool
+--
+--   NULLMX (RFC7505) records consisting of a single period must not
+--   be accepted. Moreover, the existence of a NULLMX must be reported
+--   back to the caller because the whole point of a NULLMX is that
+--   its existence should preempt an @A@ record check. We abuse the
+--   return type for this, and return @Nothing@ in the event of a
+--   NULLMX. Otherwise we return @Just True@ or @Just False@ to
+--   indicate the existence (or not) of MX records.
+--
+--   RFC7505 states that a domain MUST NOT have any other MX records
+--   if it has a NULLMX record. We enforce this. If you have a NULLMX
+--   record and some other MX record, we consider the set invalid.
+--
+validate_mx :: Resolver -> Domain -> IO (Maybe Bool)
 validate_mx resolver domain
-  | domain `elem` common_domains = return True
+  | domain `elem` common_domains = return $ Just True
   | otherwise = do
       result <- lookupMX resolver domain
       case result of
-        -- A list of one or more elements?
-        Right (_:_) -> return True
-        _           -> return False
-
+        Left _ ->
+            return $ Just False
+        Right mxs ->
+            case mxs of
+              [] -> return $ Just False
+              _  -> if any (is_null) mxs
+                   then return Nothing
+                   else return $ Just True
+  where
+    nullmx :: Domain
+    nullmx = BS.pack "."
+
+    is_null :: (Domain,Int) -> Bool
+    is_null (mx,prio) = mx == nullmx && prio == 0
 
 -- | Check whether the given domain has a valid A record.
 validate_a :: Resolver -> Domain -> IO Bool
@@ -76,26 +111,33 @@ validate_a resolver domain
 
 
 -- | Validate an email address by doing some simple syntax checks and
---   (if those fail) an MX lookup. We don't count an A record as a mail
---   exchanger.
+--   (if those fail) an MX lookup. We don't count an @A@ record as a mail
+--   exchanger unless @accept_a@ is True. And even then, the existence
+--   of a NULLMX record will preclude the acceptance of an @A@ record.
+--   The domain @example.com@ is a great test case for this behavior.
 validate :: Resolver -> Bool -> Bool -> Address -> IO (Address, Bool)
 validate resolver accept_a rfc5322 address = do
   let valid_syntax = validate_syntax rfc5322 address
   if valid_syntax then do
     let (_,domain) = parts address
     mx_result <- validate_mx resolver domain
-    if mx_result
-    then return (address, True)
-    else
-      if accept_a
-      then do
-        a_result <- validate_a resolver domain
-        return (address, a_result)
-      else
-        return (address, False)
-  else
-    return (address, False)
-
+    case mx_result of
+      Nothing  ->
+          -- NULLMX, don't fall back to 'A' records under any
+          -- circumstances.
+          return (address, False)
+      Just mxr ->
+          if mxr
+          then return (address, True)
+          else
+            if accept_a
+            then do
+              a_result <- validate_a resolver domain
+              return (address, a_result)
+            else
+              return (address, False)
+          else
+            return (address, False)
 
 
 main :: IO ()