mailcop

package module
v0.0.5 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 23, 2024 License: Apache-2.0 Imports: 12 Imported by: 0

README

mailcop

A Go package for validating email addresses according to RFC 5322 and additional rules. It uses the Go standard library for syntax validation and provides options for checking DNS records, disposable email domains, free email providers, and reserved domains.

Go Reference Go Report Card

Table of Contents

Features

  • RFC 5322 email syntax validation using the Go standard library
  • Recognizes IANA reserved domains (example.com, .test TLD, etc.)
  • Identifies disposable email domains with configurable accuracy
  • Detects free email providers
  • Optional DNS MX record validation
  • Concurrent validation for multiple addresses
  • Memory-efficient Bloom filter option for large domain lists

Installation

go get github.com/patrickward/mailcop

Basic Usage

// Create validator with default options
validator, err := mailcop.New(mailcop.DefaultOptions())
if err != nil {
    log.Fatal(err)
}

// Validate a single email
result := validator.Validate("user@example.com")
if !result.IsValid {
    log.Printf("Invalid email: %s", result.ErrorMessage())
}

// Validate multiple emails concurrently
emails := []string{
    "user1@example.com",
    "user2@gmail.com",
    "invalid@",
}
results := validator.ValidateMany(emails)

[Previous content up to Basic Usage section remains the same...]

Thread Safety

The validator is designed to be thread-safe and can be safely shared across multiple goroutines. This means you can:

  • Initialize a single validator instance at application startup
  • Share it across your entire application
  • Use it concurrently in web server handlers
  • Perform parallel validations
// Initialize once at startup
emailValidator, err := mailcop.New(mailcop.DefaultOptions())
if err != nil {
    log.Fatal(err)
}

// Use safely across multiple goroutines in your web server
http.HandleFunc("/validate", func(w http.ResponseWriter, r *http.Request) {
    email := r.FormValue("email")
    result := emailValidator.Validate(email)
    // Handle result...
})

// Or use ValidateMany for concurrent validation of multiple emails
emails := []string{
    "user1@example.com",
    "user2@gmail.com",
    "invalid@",
}
results := emailValidator.ValidateMany(emails) // Internally uses goroutines

Thread safety is ensured through:

  • Proper mutex usage for all shared state access
  • Read-write locks for optimized concurrent reads
  • Thread-safe Bloom filter implementation
  • Safe DNS cache management
  • Consistent lock ordering to prevent deadlocks

This makes the validator suitable for high-concurrency environments like web servers, API services, and background processing systems.

Configuration Options

opts := mailcop.Options{
    CheckDNS:           true,
    CheckDisposable:    true,
    CheckFreeProvider:  true,
    DNSCacheTTL:        1 * time.Hour,
    DNSCacheSize:       1000,
    DNSTimeout:         3 * time.Second,
    DisposableListURL:  "file:///path/to/disposable-domains.json",
    FreeProvidersURL:   "file:///path/to/free-providers.json",
    MaxEmailLength:     254,
    MinDomainLength:    3,
    RejectDisposable:   true,
    RejectFreeProvider: true,
    RejectIPDomains:    true,
    RejectNamedEmails:  true,
    RejectReserved:     true,
}

Validation Results

The ValidationResult struct provides detailed information:

type ValidationResult struct {
    Name           string        // Parsed name from email
    Address        string        // Normalized email address
    Original       string        // Original email address input
    IsValid        bool          // Whether the email is valid
    IsDisposable   bool          // Whether the domain is disposable
    IsFreeProvider bool          // Whether the domain is a free provider
    IsReserved     bool          // Whether the domain is reserved
    IsIPDomain     bool          // Whether the domain is an IP address
    ValidationTime time.Duration // Time taken to validate
    LastError      error         // Validation error
}

// Get error message as string
errMsg := result.ErrorMessage() // Returns empty string if no error

Advanced Features

Free Email Providers

Detection of free email providers (like Gmail, Yahoo, etc.). Multiple methods are available:

// 1. Load from URL or file during initialization
opts := mailcop.DefaultOptions()
opts.CheckFreeProvider = true
opts.RejectFreeProvider = true // Optional: reject free providers
opts.FreeProvidersURL = "file:///path/to/providers.json"
v, err := mailcop.New(opts)

// 2. Load after initialization
err = v.LoadFreeProviders("file:///path/to/providers.json")

// 3. Register providers manually
v.RegisterFreeProviders([]string{
    "gmail.com",
    "yahoo.com",
    "hotmail.com",
})
Disposable Email Domains

Detection of disposable/temporary email domains. Multiple methods are available:

// 1. Load from URL or file during initialization
opts := mailcop.DefaultOptions()
opts.CheckDisposable = true
opts.RejectDisposable = true // Optional: reject disposable domains
opts.DisposableListURL = "file:///path/to/disposable.json"
v, err := mailcop.New(opts)

// 2. Load after initialization
err = v.LoadDisposableDomains("file:///path/to/disposable.json")

// 3. Register domains manually
v.RegisterDisposableDomains([]string{
    "tempmail.com",
    "throwaway.com",
})
Trusted Domains

You can register trusted domains that will never be considered disposable, regardless of whether you're using the map or Bloom filter implementation:

// Load from JSON file/URL
err := v.LoadTrustedDomains("file:///path/to/trusted.json")
if err != nil {
    log.Fatal(err)
}

// Or register manually
v.RegisterTrustedDomains([]string{
    "gmail.com",
    "outlook.com",
})
Bloom Filter Support
What is a Bloom Filter?

A Bloom filter is a space-efficient probabilistic data structure used to test whether an element is a member of a set. It can tell us either:

  • "This element is definitely not in the set" (100% accurate)
  • "This element is probably in the set" (configurable accuracy)

This makes it perfect for disposable domain checking where:

  • We want to be certain about legitimate domains (no false negatives)
  • We can tolerate occasionally marking a legitimate domain as disposable (false positives)
  • Memory efficiency is important
Basic Setup
// Create validator with disposable checking enabled
opts := mailcop.DefaultOptions()
opts.CheckDisposable = true
v, err := mailcop.New(opts)

// Register any trusted domains (optional)
v.RegisterTrustedDomains([]string{"gmail.com"})

// Configure and enable Bloom filter
bloomOpts := mailcop.DefaultBloomOptions()
err = v.UseBloomFilter("file:///path/to/domains.json", bloomOpts)
if err != nil {
    log.Fatal(err)
}

// Optional: Save filter to file for later use
f, err := os.Create("filter.bloom")
if err != nil {
log.Fatal(err)
}
defer f.Close()
err = v.SaveBloomFilter(f)

// Optional: Load existing filter
f, err := os.Open("filter.bloom")
if err != nil {
log.Fatal(err)
}
defer f.Close()
err = v.LoadBloomFilter(f)
Configuration Options
type BloomOptions struct {
// FalsePositiveRate sets the desired false positive rate (0.0 to 1.0)
// Lower values use more memory but have fewer false positives
FalsePositiveRate float64

// VerificationAttempts reduces false positives exponentially by checking
// the domain multiple times with different hash functions. Each check must
// return "probably in set" for the domain to be considered disposable.
// The actual false positive rate becomes FalsePositiveRate^VerificationAttempts.
//
// Examples with FalsePositiveRate = 0.01 (1%):
// - 1 attempt: 1% false positives (0.01^1)
// - 2 attempts: 0.01% false positives (0.01^2)
// - 3 attempts: 0.0001% false positives (0.01^3)
//
// Higher values provide better accuracy at the cost of more CPU time.
// Default is 1.
VerificationAttempts int
}

This allows you to balance memory usage and accuracy:

// High memory efficiency, using multiple checks for accuracy
bloomOpts := mailcop.DefaultBloomOptions()
bloomOpts.FalsePositiveRate = 0.01 // Use less memory (1% base rate)
bloomOpts.VerificationAttempts = 2 // Reduces to 0.01% actual false positives

// High memory usage, single check
bloomOpts := mailcop.DefaultBloomOptions()
bloomOpts.FalsePositiveRate = 0.001 // Use more memory (0.1% false positives)
bloomOpts.VerificationAttempts = 1  // Single check

// With trusted domains
bloomOpts.TrustedDomains = map[string]struct{}{
"gmail.com": {},
"outlook.com": {},
}

Domain Lists

Disposable Email Domains

Several community-maintained lists are available:

Free Email Providers

Some resources for free email provider lists:

API Reference

Validator Methods
// Create new validator
New(options Options) (*Validator, error)

// Validation
Validate(email string) ValidationResult
ValidateMany(emails []string) []ValidationResult

// Domain Management
LoadDisposableDomains(url string) error
LoadFreeProviders(url string) error
LoadTrustedDomains(url string) error
RegisterDisposableDomains(domains []string)
RegisterFreeProviders(providers []string)
RegisterTrustedDomains(domains []string)

// Bloom Filter
UseBloomFilter(url string, opts BloomOptions) error
SaveBloomFilter(w io.Writer) error
LoadBloomFilter(r io.Reader) error
ValidationResult Methods
// Get error message as string
ErrorMessage() string
Configuration Options
type BloomOptions struct {
    // FalsePositiveRate sets the desired false positive rate (0.0 to 1.0)
    // Lower values use more memory but have fewer false positives
    FalsePositiveRate float64

    // TrustedDomains are never marked as disposable
    TrustedDomains map[string]struct{}

    // VerificationAttempts reduces false positives exponentially
    // - 1 check: FalsePositiveRate
    // - 2 checks: FalsePositiveRate^2
    // - 3 checks: FalsePositiveRate^3
    VerificationAttempts int
}
Configuring False Positives

Balance memory usage and accuracy:

// High accuracy, more memory
bloomOpts := mailcop.DefaultBloomOptions()
bloomOpts.FalsePositiveRate = 0.001     // 0.1%
bloomOpts.VerificationAttempts = 1

// Less memory, multiple verifications
bloomOpts := mailcop.DefaultBloomOptions()
bloomOpts.FalsePositiveRate = 0.01      // 1%
bloomOpts.VerificationAttempts = 2       // Reduces to 0.01%

// With trusted domains
bloomOpts.TrustedDomains = map[string]struct{}{
    "gmail.com": {},
    "outlook.com": {},
}

Performance Considerations

Memory Usage

Based on benchmarks with ~240k domains:

Implementation Memory False Positives Use Case
Map ~5 MB None Perfect accuracy needed
Bloom (0.1%) ~300KB 1 in 1,000 Memory-constrained systems
Bloom (1%) ~150KB 1 in 100 Extreme memory constraints
Lookup Performance
  • Map: Constant time O(1)
  • Bloom Filter:
    • O(k) where k is VerificationAttempts
    • Additional hash calculations
    • Still microsecond-range performance
Choosing an Implementation

Use the Bloom filter when:

  • Memory is constrained
  • You have many domains (>10,000)
  • Occasional false positives are acceptable
  • You can maintain a trusted domains list

Use the map implementation when:

  • Perfect accuracy is required
  • Memory isn't constrained
  • You have fewer domains (<10,000)
  • You need frequent domain updates
File Format

Both implementations accept JSON files containing an array of domains:

[
    "disposable1.com",
    "disposable2.com",
    "tempmail.org"
]

Files can be loaded from local filesystem or URLs:

// Local file
v.UseBloomFilter("file:///path/to/domains.json", bloomOpts)

// Remote URL
v.UseBloomFilter("https://example.com/domains.json", bloomOpts)

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func DefaultFreeProviders

func DefaultFreeProviders() map[string]struct{}

DefaultFreeProviders returns the default free email providers

Types

type BloomOptions added in v0.0.4

type BloomOptions struct {
	// FalsePositiveRate sets the desired false positive rate (0.0 to 1.0)
	// Lower values use more memory but have fewer false positives
	FalsePositiveRate float64

	// VerificationAttempts reduces false positives exponentially by checking
	// the domain multiple times with different hash functions. Each check must
	// return "probably in set" for the domain to be considered disposable.
	// The actual false positive rate becomes FalsePositiveRate^VerificationAttempts.
	//
	// Examples with FalsePositiveRate = 0.01 (1%):
	// - 1 attempt: 1% false positives (0.01^1)
	// - 2 attempts: 0.01% false positives (0.01^2)
	// - 3 attempts: 0.0001% false positives (0.01^3)
	//
	// Higher values provide better accuracy at the cost of more CPU time.
	// Default is 1.
	VerificationAttempts int
}

BloomOptions configures the bloom filter behavior

func DefaultBloomOptions added in v0.0.4

func DefaultBloomOptions() BloomOptions

DefaultBloomOptions returns sensible defaults

type Options

type Options struct {
	CheckDNS             bool          // Whether to perform DNS MX lookup
	CheckDisposable      bool          // Whether to check for disposable domains
	CheckFreeProvider    bool          // Whether to check for free email providers
	DNSCacheTTL          time.Duration // TTL for DNS cache
	DNSCacheSize         int           // Maximum number of DNS cache entries
	DNSTimeout           time.Duration // Timeout for DNS lookups
	DisposableDomainsURL string        // URL for disposable domains list
	FreeProvidersURL     string        // URL for free email providers list
	MaxEmailLength       int           // Maximum email length
	MinDomainLength      int           // Minimum domain length
	RejectDisposable     bool          // Whether to invalidate disposable domains
	RejectFreeProvider   bool          // Whether to invalidate free email providers
	RejectIPDomains      bool          // Whether to reject IP address domains
	RejectNamedEmails    bool          // Whether to reject named email addresses (e.g. "First Last <first.last@example.com>")
	RejectReserved       bool          // Whether to invalidate reserved example domains
	TrustedDomainsURL    string        // URL for trusted domains list
}

Options contains configuration options for email validation

func DefaultOptions

func DefaultOptions() Options

DefaultOptions returns the default validator options

type ValidationResult

type ValidationResult struct {
	Address        string        // Normalized email address
	IsDisposable   bool          // Whether the domain is disposable
	IsFreeProvider bool          // Whether the domain is a free provider
	IsIPDomain     bool          // Whether the domain is an IP address
	IsReserved     bool          // Whether the domain is reserved
	IsValid        bool          // Whether the email is valid
	LastError      error         // Validation error
	Name           string        // Parsed name from email
	Original       string        // Original email address input
	ValidationTime time.Duration // Time taken to validate
}

func (ValidationResult) ErrorMessage added in v0.0.3

func (vr ValidationResult) ErrorMessage() string

ErrorMessage returns the last validation error as a string if present, otherwise an empty string

type Validator

type Validator struct {
	// contains filtered or unexported fields
}

func New

func New(options Options) (*Validator, error)

func (*Validator) IsValid added in v0.0.5

func (v *Validator) IsValid(email string) bool

IsValid checks if an email address is valid and immediately returns a boolean

func (*Validator) LoadBloomFilter added in v0.0.4

func (v *Validator) LoadBloomFilter(r io.Reader) error

LoadBloomFilter deserializes the bloom filter from the provided reader

func (*Validator) LoadDisposableDomains

func (v *Validator) LoadDisposableDomains(urlStr string) error

LoadDisposableDomains loads domains from a JSON array into either the map or bloom filter, depending on which implementation is being used

func (*Validator) LoadFreeProviders

func (v *Validator) LoadFreeProviders(urlStr string) error

LoadFreeProviders loads a list of free email providers from a JSON file or URL

func (*Validator) LoadTrustedDomains added in v0.0.4

func (v *Validator) LoadTrustedDomains(urlStr string) error

LoadTrustedDomains loads a list of trusted domains from a JSON file or URL

func (*Validator) RegisterDisposableDomains

func (v *Validator) RegisterDisposableDomains(domains []string)

RegisterDisposableDomains adds domains to either the map or bloom filter

func (*Validator) RegisterFreeProviders

func (v *Validator) RegisterFreeProviders(providers []string)

RegisterFreeProviders manually adds domains to the free providers list

func (*Validator) RegisterTrustedDomains added in v0.0.4

func (v *Validator) RegisterTrustedDomains(domains []string)

RegisterTrustedDomains adds trusted domains that are never considered disposable

func (*Validator) SaveBloomFilter added in v0.0.4

func (v *Validator) SaveBloomFilter(w io.Writer) error

SaveBloomFilter serializes the bloom filter to the provided writer

func (*Validator) UseBloomFilter added in v0.0.4

func (v *Validator) UseBloomFilter(url string, opts BloomOptions) error

UseBloomFilter converts the validator to use a bloom filter instead of a map for disposable domain checking. This can significantly reduce memory usage. The expectedItems parameter should be set to the approximate number of disposable domains you expect to add to the filter.

func (*Validator) Validate

func (v *Validator) Validate(email string) ValidationResult

Validate checks a single email address

func (*Validator) ValidateMany

func (v *Validator) ValidateMany(emails []string) []ValidationResult

ValidateMany validates multiple email addresses concurrently

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL