Tools/SmtpServer.ps1

function Start-SmtpServer
{
    $script = {
        # scriptblock for the core smtp message processing logic
        $process = {
            # if there's no client, just return
            if ($PodeSession.Tcp.Client -eq $null) {
                return
            }

            # variables to store data for later processing
            $mail_from = [string]::Empty
            $rcpt_tos = @()
            $data = [string]::Empty

            # open response to smtp request
            tcp write "220 $($PodeSession.IP.Name) -- Pode Proxy Server"
            $msg = [string]::Empty

            # respond to smtp request
            while ($true)
            {
                try { $msg = (tcp read) }
                catch { break }

                try {
                    if (!(Test-Empty $msg)) {
                        if ($msg.StartsWith('QUIT')) {
                            tcp write '221 Bye'

                            if ($Client -ne $null -and $Client.Connected) {
                                try {
                                    $Client.Close()
                                    $Client.Dispose()
                                } catch { }
                            }

                            break
                        }

                        if ($msg.StartsWith('EHLO') -or $msg.StartsWith('HELO')) {
                            tcp write '250 OK'
                        }

                        if ($msg.StartsWith('RCPT TO')) {
                            tcp write '250 OK'
                            $rcpt_tos += (Get-SmtpEmail $msg)
                        }

                        if ($msg.StartsWith('MAIL FROM')) {
                            tcp write '250 OK'
                            $mail_from = Get-SmtpEmail $msg
                        }

                        if ($msg.StartsWith('DATA'))
                        {
                            tcp write '354 Start mail input; end with <CR><LF>.<CR><LF>'
                            $data = (tcp read)
                            tcp write '250 OK'

                            # set session data
                            $PodeSession.Smtp.From = $mail_from
                            $PodeSession.Smtp.To = $rcpt_tos
                            $PodeSession.Smtp.Data = $data
                            $PodeSession.Smtp.Lockable = $PodeSession.Lockable

                            # call user handlers for processing smtp data
                            Invoke-ScriptBlock -ScriptBlock (Get-PodeTcpHandler -Type 'SMTP') -Arguments $PodeSession.Smtp -Scoped
                        }
                    }
                }
                catch [exception] {
                    throw $_.exception
                }
            }
        }

        # setup and run the smtp listener
        try
        {
            # ensure we have smtp handlers
            if ((Get-PodeTcpHandler -Type 'SMTP') -eq $null) {
                throw 'No SMTP handler has been passed'
            }

            # grab the relavant port
            $port = $PodeSession.IP.Port
            if ($port -eq 0) {
                $port = 25
            }

            $endpoint = New-Object System.Net.IPEndPoint($PodeSession.IP.Address, $port)
            $listener = New-Object System.Net.Sockets.TcpListener -ArgumentList $endpoint

            # start listener
            $listener.Start()

            # state where we're running
            Write-Host "Listening on smtp://$($PodeSession.IP.Name):$($port)" -ForegroundColor Yellow

            # loop for tcp request
            while ($true)
            {
                $task = $listener.AcceptTcpClientAsync()
                $task.Wait($PodeSession.Tokens.Cancellation.Token)
                $client = $task.Result

                # ensure the request ip is allowed
                if (!(Test-IPAccess -IP (ConvertTo-IPAddress -Endpoint $client.Client.RemoteEndPoint))) {
                    try {
                        $client.Close()
                        $client.Dispose()
                    } catch { }
                }

                # deal with smtp call
                else {
                    $PodeSession.Tcp.Client = $client
                    $PodeSession.Smtp = @{}
                    Invoke-ScriptBlock -ScriptBlock $process
                }
            }
        }
        catch [System.OperationCanceledException] {}
        catch {
            $Error[0] | Out-Default
            throw $_.Exception
        }
        finally {
            if ($listener -ne $null) {
                $listener.Stop()
            }
        }
    }

    Add-PodeRunspace $script
}


function Get-SmtpEmail
{
    param (
        [Parameter()]
        [string]
        $Value
    )

    $tmp = ($Value -isplit ':')
    if ($tmp.Length -gt 1) {
        return $tmp[1].Trim().Trim('<', '>')
    }

    return [string]::Empty
}