hsemail-1.7.7: Internet Message Parsers

Copyright | (c) 2013 Peter Simons
License | BSD3
Maintainer | simons@cryp.to
Stability | provisional
Portability | portable
Safe Haskell | Safe
Language | Haskell98

Text.ParserCombinators.Parsec.Rfc2821

Contents

- ESMTP State Machine
- Data Types for SMTP Commands
- Data Types for SMTP Replies
- Command Parsers
- Argument Parsers
- Helper Functions

Description

This module exports parser combinators for the grammar described in RFC2821, "Simple Mail Transfer Protocol", http://www.faqs.org/rfcs/rfc2821.html.

Synopsis

- data SessionState
  - = Unknown
  - | HaveHelo
  - | HaveMailFrom
  - | HaveRcptTo
  - | HaveData
  - | HaveQuit
- data Event
  - = Greeting
  - | SayHelo String
  - | SayHeloAgain String
  - | SayEhlo String
  - | SayEhloAgain String
  - | SetMailFrom Mailbox
  - | AddRcptTo Mailbox
  - | StartData
  - | Deliver
  - | NeedHeloFirst
  - | NeedMailFromFirst
  - | NeedRcptToFirst
  - | NotImplemened
  - | ResetState
  - | SayOK
  - | SeeksHelp String
  - | Shutdown
  - | SyntaxErrorIn String
  - | Unrecognized String
- type SmtpdFSM = State SessionState Event
- smtpdFSM :: String -> SmtpdFSM
- handleSmtpCmd :: SmtpCmd -> SmtpdFSM
- data SmtpCmd
  - = Helo String
  - | Ehlo String
  - | MailFrom Mailbox
  - | RcptTo Mailbox
  - | Data
  - | Rset
  - | Send Mailbox
  - | Soml Mailbox
  - | Saml Mailbox
  - | Vrfy String
  - | Expn String
  - | Help String
  - | Noop
  - | Quit
  - | Turn
  - | WrongArg String ParseError
- data Mailbox = Mailbox [String] String String
- nullPath :: Mailbox
- postmaster :: Mailbox
- data SmtpReply = Reply SmtpCode [String]
- data SmtpCode = Code SuccessCode Category Int
- data SuccessCode
  - = Unused0
  - | PreliminarySuccess
  - | Success
  - | IntermediateSuccess
  - | TransientFailure
  - | PermanentFailure
- data Category
  - = Syntax
  - | Information
  - | Connection
  - | Unspecified3
  - | Unspecified4
  - | MailSystem
- reply :: Int -> Int -> Int -> [String] -> SmtpReply
- isSuccess :: SmtpReply -> Bool
- isFailure :: SmtpReply -> Bool
- isShutdown :: SmtpReply -> Bool
- type SmtpParser st = CharParser st SmtpCmd
- smtpCmd :: SmtpParser st
- smtpData :: SmtpParser st
- rset :: SmtpParser st
- quit :: SmtpParser st
- turn :: SmtpParser st
- helo :: SmtpParser st
- ehlo :: SmtpParser st
- mail :: SmtpParser st
- rcpt :: SmtpParser st
- send :: SmtpParser st
- soml :: SmtpParser st
- saml :: SmtpParser st
- vrfy :: SmtpParser st
- expn :: SmtpParser st
- help :: SmtpParser st
- noop :: SmtpParser st
- from_path :: CharParser st Mailbox
- to_path :: CharParser st Mailbox
- path :: CharParser st Mailbox
- mailbox :: CharParser st Mailbox
- local_part :: CharParser st String
- domain :: CharParser st String
- a_d_l :: CharParser st [String]
- at_domain :: CharParser st String
- address_literal :: CharParser st String
- ipv4_literal :: CharParser st String
- ipv4addr :: CharParser st String
- subdomain :: CharParser st String
- dot_string :: CharParser st String
- atom :: CharParser a String
- snum :: CharParser st String
- number :: CharParser st String
- word :: CharParser st String
- fixCRLF :: String -> String
- mkCmd0 :: String -> a -> CharParser st a
- mkCmd1 :: String -> (a -> SmtpCmd) -> CharParser st a -> CharParser st SmtpCmd
- tokenList :: CharParser st String -> Char -> CharParser st String

# ESMTP State Machine

data SessionState Source

Constructors

Unknown |
HaveHelo |
HaveMailFrom |
HaveRcptTo |
HaveData |
HaveQuit |

Instances

Bounded SessionState Source |
Enum SessionState Source |
Eq SessionState Source |
Ord SessionState Source |
Show SessionState Source |

data Event Source

Constructors

Greeting | reserved for the user
SayHelo String |
SayHeloAgain String |
SayEhlo String |
SayEhloAgain String |
SetMailFrom Mailbox |
AddRcptTo Mailbox |
StartData |
Deliver | reserved for the user
NeedHeloFirst |
NeedMailFromFirst |
NeedRcptToFirst |
NotImplemened | `Turn`, `Send`, `Soml`, `Saml`, `Vrfy`, and `Expn`.
ResetState |
SayOK | Triggered in case of `Noop` or when `Rset` is used before we even have a state.
SeeksHelp String | The parameter may be `[]`.
Shutdown |
SyntaxErrorIn String |
Unrecognized String |

Instances

Eq Event Source |
Show Event Source |

type SmtpdFSM = State SessionState Event Source

smtpdFSM :: String -> SmtpdFSM Source

Parse a line of SMTP dialogue and run `handleSmtpCmd` to determine the `Event`. In case of syntax errors, `SyntaxErrorIn` or `Unrecognized` will be returned. Inputs must be terminated with `crlf`. See `fixCRLF`.

handleSmtpCmd :: SmtpCmd -> SmtpdFSM Source

For those who want to parse the `SmtpCmd` themselves. Calling this function in `HaveQuit` or `HaveData` will fail an assertion. If `assert` is disabled, it will return respectively `Shutdown` and `StartData` again.

# Data Types for SMTP Commands

data SmtpCmd Source

The `smtpCmd` parser will create this data type from a string. Note that all command parsers expect their input to be terminated with `crlf`.

Constructors

Helo String |
Ehlo String |
MailFrom Mailbox | Might be `nullPath`.
RcptTo Mailbox | Might be `postmaster`.
Data |
Rset |
Send Mailbox |
Soml Mailbox |
Saml Mailbox |
Vrfy String |
Expn String |
Help String | Might be `[]`.
Noop | Optional argument ignored.
Quit |
Turn |
WrongArg String ParseError | When a valid command has been recognized, but the argument parser fails, then this type will be returned. The `String` contains the name of the command (in all upper-case) and the `ParseError` is, obviously, the error description.

Instances

Show SmtpCmd Source |

data Mailbox Source

The most general e-mail address has the form: `<[@route,...:]user@domain>`. This type, too, supports `show` and `read`. Note that a "shown" address is always enclosed in angular brackets. When comparing two mailboxes for equality, the hostname is case-insensitive.

Constructors

Mailbox [String] String String |

Instances

Eq Mailbox Source |
Read Mailbox Source |
Show Mailbox Source |

nullPath :: Mailbox Source

`nullPath` `=` ``Mailbox` [] "" "" = "<>"`

postmaster :: Mailbox Source

`postmaster` `=` ``Mailbox` [] "postmaster" "" = "<postmaster>"`

# Data Types for SMTP Replies

data SmtpReply Source

An SMTP reply is a three-digit return code plus some waste of bandwidth called "comments". This is what the list of strings is for; one string per line in the reply. `show` will append an "`\r\n`" end-of-line marker to each entry in that list, so that the resulting string is ready to be sent back to the peer. For example:

    >>> show $ Reply (Code Success MailSystem 0) ["worked", "like", "a charm" ]
    "250-worked\r\n250-like\r\n250 a charm\r\n"

If the message is an empty list `[]`, a default text will be constructed:

    >>> show $ Reply (Code Success MailSystem 0) []
    "250 Success in category MailSystem\r\n"

Constructors

Reply SmtpCode [String] |

Instances

Show SmtpReply Source |

data SmtpCode Source

Constructors

Code SuccessCode Category Int |

Instances

Show SmtpCode Source |

data SuccessCode Source

Constructors

Unused0 |
PreliminarySuccess |
Success |
IntermediateSuccess |
TransientFailure |
PermanentFailure |

Instances

Bounded SuccessCode Source |
Enum SuccessCode Source |
Eq SuccessCode Source |
Ord SuccessCode Source |
Show SuccessCode Source |

data Category Source

Constructors

Syntax |
Information |
Connection |
Unspecified3 |
Unspecified4 |
MailSystem |

Instances

Bounded Category Source |
Enum Category Source |
Eq Category Source |
Ord Category Source |
Show Category Source |

reply :: Int -> Int -> Int -> [String] -> SmtpReply Source

Construct a `Reply`. Fails `assert` if invalid numbers are given.

isSuccess :: SmtpReply -> Bool Source

A reply constitutes "success" if the status code is any of `PreliminarySuccess`, `Success`, or `IntermediateSuccess`.

isFailure :: SmtpReply -> Bool Source

A reply constitutes "failure" if the status code is either `PermanentFailure` or `TransientFailure`.

isShutdown :: SmtpReply -> Bool Source

The replies `221` and `421` signify `Shutdown`.

# Command Parsers

type SmtpParser st = CharParser st SmtpCmd Source

The SMTP parsers defined here correspond to the commands specified in RFC2821, so I won't document them individually.

smtpCmd :: SmtpParser st Source

This parser recognizes any of the SMTP commands defined below. Note that all command parsers expect their input to be terminated with `crlf`.

smtpData :: SmtpParser st Source

The parser name "data" was taken.

rset :: SmtpParser st Source

quit :: SmtpParser st Source

turn :: SmtpParser st Source

helo :: SmtpParser st Source

ehlo :: SmtpParser st Source

mail :: SmtpParser st Source

rcpt :: SmtpParser st Source

send :: SmtpParser st Source

soml :: SmtpParser st Source

saml :: SmtpParser st Source

vrfy :: SmtpParser st Source

expn :: SmtpParser st Source

help :: SmtpParser st Source

noop :: SmtpParser st Source

May have an optional `word` argument, but it is ignored.

# Argument Parsers

from_path :: CharParser st Mailbox Source

to_path :: CharParser st Mailbox Source

path :: CharParser st Mailbox Source

mailbox :: CharParser st Mailbox Source

local_part :: CharParser st String Source

domain :: CharParser st String Source

a_d_l :: CharParser st [String] Source

at_domain :: CharParser st String Source

address_literal :: CharParser st String Source

TODO: Add IPv6 address and general literals

ipv4_literal :: CharParser st String Source

ipv4addr :: CharParser st String Source

subdomain :: CharParser st String Source

dot_string :: CharParser st String Source

atom :: CharParser a String Source

snum :: CharParser st String Source

number :: CharParser st String Source

word :: CharParser st String Source

This is a useful addition: The parser accepts an `atom` or a `quoted_string`.

# Helper Functions

fixCRLF :: String -> String Source

Make the string `crlf` terminated no matter what. '`\n`' is expanded, otherwise `crlf` is appended. Note that if the string was terminated incorrectly before, it still is. This function is useful when reading input with `hGetLine` which removes the end-of-line delimiter.

mkCmd0 :: String -> a -> CharParser st a Source

Construct a parser for a command without arguments. Expects `crlf`!

mkCmd1 :: String -> (a -> SmtpCmd) -> CharParser st a -> CharParser st SmtpCmd Source

Construct a parser for a command with an argument, which the given parser will handle. The result of the argument parser will be applied to the type constructor before it is returned. Expects `crlf`!

tokenList :: CharParser st String -> Char -> CharParser st String Source