Embed inline attachments into HTML bodies with a new Send Mail Message function.

#requires -Version 2.0

function Send-MailMessage
{
    [CmdletBinding()]
    param(
        [Parameter(ValueFromPipeline=$true)]
        [Alias('PsPath')]
        [ValidateNotNullOrEmpty()]
        [string[]]
        ${Attachments},

        [ValidateNotNullOrEmpty()]
        [Collections.HashTable]
        ${InlineAttachments},

        [ValidateNotNullOrEmpty()]
        [Net.Mail.MailAddress[]]
        ${Bcc},

        [Parameter(Position=2)]
        [ValidateNotNullOrEmpty()]
        [string]
        ${Body},

        [Alias('BAH')]
        [switch]
        ${BodyAsHtml},

        [ValidateNotNullOrEmpty()]
        [Net.Mail.MailAddress[]]
        ${Cc},

        [Alias('DNO')]
        [ValidateNotNullOrEmpty()]
        [Net.Mail.DeliveryNotificationOptions]
        ${DeliveryNotificationOption},

        [Parameter(Mandatory=$true)]
        [ValidateNotNullOrEmpty()]
        [Net.Mail.MailAddress]
        ${From},

        [Parameter(Mandatory = $true, Position = 3)]
        [Alias('ComputerName')]
        [string]
        ${SmtpServer},

        [ValidateNotNullOrEmpty()]
        [Net.Mail.MailPriority]
        ${Priority},

        [Parameter(Mandatory=$true, Position=1)]
        [Alias('sub')]
        [ValidateNotNullOrEmpty()]
        [string]
        ${Subject},

        [Parameter(Mandatory=$true, Position=0)]
        [Net.Mail.MailAddress[]]
        ${To},

        [ValidateNotNullOrEmpty()]
        [Management.Automation.PSCredential]
        ${Credential},

        [switch]
        ${UseSsl},

        [ValidateRange(0, 2147483647)]
        [int]
        ${Port} = 25
    )

    begin
    {
        function FileNameToContentType
        {
            [CmdletBinding()]
            param (
                [Parameter(Mandatory = $true)]
                [string]
                $FileName
            )

            $mimeMappings = @{
                '.323'         = 'text/h323'
                '.aaf'         = 'application/octet-stream'
                '.aca'         = 'application/octet-stream'
                '.accdb'       = 'application/msaccess'
                '.accde'       = 'application/msaccess'
                '.accdt'       = 'application/msaccess'
                '.acx'         = 'application/internet-property-stream'
                '.afm'         = 'application/octet-stream'
                '.ai'          = 'application/postscript'
                '.aif'         = 'audio/x-aiff'
                '.aifc'        = 'audio/aiff'
                '.aiff'        = 'audio/aiff'
                '.application' = 'application/x-ms-application'
                '.art'         = 'image/x-jg'
                '.asd'         = 'application/octet-stream'
                '.asf'         = 'video/x-ms-asf'
                '.asi'         = 'application/octet-stream'
                '.asm'         = 'text/plain'
                '.asr'         = 'video/x-ms-asf'
                '.asx'         = 'video/x-ms-asf'
                '.atom'        = 'application/atom+xml'
                '.au'          = 'audio/basic'
                '.avi'         = 'video/x-msvideo'
                '.axs'         = 'application/olescript'
                '.bas'         = 'text/plain'
                '.bcpio'       = 'application/x-bcpio'
                '.bin'         = 'application/octet-stream'
                '.bmp'         = 'image/bmp'
                '.c'           = 'text/plain'
                '.cab'         = 'application/octet-stream'
                '.calx'        = 'application/vnd.ms-office.calx'
                '.cat'         = 'application/vnd.ms-pki.seccat'
                '.cdf'         = 'application/x-cdf'
                '.chm'         = 'application/octet-stream'
                '.class'       = 'application/x-java-applet'
                '.clp'         = 'application/x-msclip'
                '.cmx'         = 'image/x-cmx'
                '.cnf'         = 'text/plain'
                '.cod'         = 'image/cis-cod'
                '.cpio'        = 'application/x-cpio'
                '.cpp'         = 'text/plain'
                '.crd'         = 'application/x-mscardfile'
                '.crl'         = 'application/pkix-crl'
                '.crt'         = 'application/x-x509-ca-cert'
                '.csh'         = 'application/x-csh'
                '.css'         = 'text/css'
                '.csv'         = 'application/octet-stream'
                '.cur'         = 'application/octet-stream'
                '.dcr'         = 'application/x-director'
                '.deploy'      = 'application/octet-stream'
                '.der'         = 'application/x-x509-ca-cert'
                '.dib'         = 'image/bmp'
                '.dir'         = 'application/x-director'
                '.disco'       = 'text/xml'
                '.dll'         = 'application/x-msdownload'
                '.dll.config'  = 'text/xml'
                '.dlm'         = 'text/dlm'
                '.doc'         = 'application/msword'
                '.docm'        = 'application/vnd.ms-word.document.macroEnabled.12'
                '.docx'        = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
                '.dot'         = 'application/msword'
                '.dotm'        = 'application/vnd.ms-word.template.macroEnabled.12'
                '.dotx'        = 'application/vnd.openxmlformats-officedocument.wordprocessingml.template'
                '.dsp'         = 'application/octet-stream'
                '.dtd'         = 'text/xml'
                '.dvi'         = 'application/x-dvi'
                '.dwf'         = 'drawing/x-dwf'
                '.dwp'         = 'application/octet-stream'
                '.dxr'         = 'application/x-director'
                '.eml'         = 'message/rfc822'
                '.emz'         = 'application/octet-stream'
                '.eot'         = 'application/octet-stream'
                '.eps'         = 'application/postscript'
                '.etx'         = 'text/x-setext'
                '.evy'         = 'application/envoy'
                '.exe'         = 'application/octet-stream'
                '.exe.config'  = 'text/xml'
                '.fdf'         = 'application/vnd.fdf'
                '.fif'         = 'application/fractals'
                '.fla'         = 'application/octet-stream'
                '.flr'         = 'x-world/x-vrml'
                '.flv'         = 'video/x-flv'
                '.gif'         = 'image/gif'
                '.gtar'        = 'application/x-gtar'
                '.gz'          = 'application/x-gzip'
                '.h'           = 'text/plain'
                '.hdf'         = 'application/x-hdf'
                '.hdml'        = 'text/x-hdml'
                '.hhc'         = 'application/x-oleobject'
                '.hhk'         = 'application/octet-stream'
                '.hhp'         = 'application/octet-stream'
                '.hlp'         = 'application/winhlp'
                '.hqx'         = 'application/mac-binhex40'
                '.hta'         = 'application/hta'
                '.htc'         = 'text/x-component'
                '.htm'         = 'text/html'
                '.html'        = 'text/html'
                '.htt'         = 'text/webviewhtml'
                '.hxt'         = 'text/html'
                '.ico'         = 'image/x-icon'
                '.ics'         = 'application/octet-stream'
                '.ief'         = 'image/ief'
                '.iii'         = 'application/x-iphone'
                '.inf'         = 'application/octet-stream'
                '.ins'         = 'application/x-internet-signup'
                '.isp'         = 'application/x-internet-signup'
                '.IVF'         = 'video/x-ivf'
                '.jar'         = 'application/java-archive'
                '.java'        = 'application/octet-stream'
                '.jck'         = 'application/liquidmotion'
                '.jcz'         = 'application/liquidmotion'
                '.jfif'        = 'image/pjpeg'
                '.jpb'         = 'application/octet-stream'
                '.jpe'         = 'image/jpeg'
                '.jpeg'        = 'image/jpeg'
                '.jpg'         = 'image/jpeg'
                '.js'          = 'application/x-javascript'
                '.jsx'         = 'text/jscript'
                '.latex'       = 'application/x-latex'
                '.lit'         = 'application/x-ms-reader'
                '.lpk'         = 'application/octet-stream'
                '.lsf'         = 'video/x-la-asf'
                '.lsx'         = 'video/x-la-asf'
                '.lzh'         = 'application/octet-stream'
                '.m13'         = 'application/x-msmediaview'
                '.m14'         = 'application/x-msmediaview'
                '.m1v'         = 'video/mpeg'
                '.m3u'         = 'audio/x-mpegurl'
                '.man'         = 'application/x-troff-man'
                '.manifest'    = 'application/x-ms-manifest'
                '.map'         = 'text/plain'
                '.mdb'         = 'application/x-msaccess'
                '.mdp'         = 'application/octet-stream'
                '.me'          = 'application/x-troff-me'
                '.mht'         = 'message/rfc822'
                '.mhtml'       = 'message/rfc822'
                '.mid'         = 'audio/mid'
                '.midi'        = 'audio/mid'
                '.mix'         = 'application/octet-stream'
                '.mmf'         = 'application/x-smaf'
                '.mno'         = 'text/xml'
                '.mny'         = 'application/x-msmoney'
                '.mov'         = 'video/quicktime'
                '.movie'       = 'video/x-sgi-movie'
                '.mp2'         = 'video/mpeg'
                '.mp3'         = 'audio/mpeg'
                '.mpa'         = 'video/mpeg'
                '.mpe'         = 'video/mpeg'
                '.mpeg'        = 'video/mpeg'
                '.mpg'         = 'video/mpeg'
                '.mpp'         = 'application/vnd.ms-project'
                '.mpv2'        = 'video/mpeg'
                '.ms'          = 'application/x-troff-ms'
                '.msi'         = 'application/octet-stream'
                '.mso'         = 'application/octet-stream'
                '.mvb'         = 'application/x-msmediaview'
                '.mvc'         = 'application/x-miva-compiled'
                '.nc'          = 'application/x-netcdf'
                '.nsc'         = 'video/x-ms-asf'
                '.nws'         = 'message/rfc822'
                '.ocx'         = 'application/octet-stream'
                '.oda'         = 'application/oda'
                '.odc'         = 'text/x-ms-odc'
                '.ods'         = 'application/oleobject'
                '.one'         = 'application/onenote'
                '.onea'        = 'application/onenote'
                '.onetoc'      = 'application/onenote'
                '.onetoc2'     = 'application/onenote'
                '.onetmp'      = 'application/onenote'
                '.onepkg'      = 'application/onenote'
                '.osdx'        = 'application/opensearchdescription+xml'
                '.p10'         = 'application/pkcs10'
                '.p12'         = 'application/x-pkcs12'
                '.p7b'         = 'application/x-pkcs7-certificates'
                '.p7c'         = 'application/pkcs7-mime'
                '.p7m'         = 'application/pkcs7-mime'
                '.p7r'         = 'application/x-pkcs7-certreqresp'
                '.p7s'         = 'application/pkcs7-signature'
                '.pbm'         = 'image/x-portable-bitmap'
                '.pcx'         = 'application/octet-stream'
                '.pcz'         = 'application/octet-stream'
                '.pdf'         = 'application/pdf'
                '.pfb'         = 'application/octet-stream'
                '.pfm'         = 'application/octet-stream'
                '.pfx'         = 'application/x-pkcs12'
                '.pgm'         = 'image/x-portable-graymap'
                '.pko'         = 'application/vnd.ms-pki.pko'
                '.pma'         = 'application/x-perfmon'
                '.pmc'         = 'application/x-perfmon'
                '.pml'         = 'application/x-perfmon'
                '.pmr'         = 'application/x-perfmon'
                '.pmw'         = 'application/x-perfmon'
                '.png'         = 'image/png'
                '.pnm'         = 'image/x-portable-anymap'
                '.pnz'         = 'image/png'
                '.pot'         = 'application/vnd.ms-powerpoint'
                '.potm'        = 'application/vnd.ms-powerpoint.template.macroEnabled.12'
                '.potx'        = 'application/vnd.openxmlformats-officedocument.presentationml.template'
                '.ppam'        = 'application/vnd.ms-powerpoint.addin.macroEnabled.12'
                '.ppm'         = 'image/x-portable-pixmap'
                '.pps'         = 'application/vnd.ms-powerpoint'
                '.ppsm'        = 'application/vnd.ms-powerpoint.slideshow.macroEnabled.12'
                '.ppsx'        = 'application/vnd.openxmlformats-officedocument.presentationml.slideshow'
                '.ppt'         = 'application/vnd.ms-powerpoint'
                '.pptm'        = 'application/vnd.ms-powerpoint.presentation.macroEnabled.12'
                '.pptx'        = 'application/vnd.openxmlformats-officedocument.presentationml.presentation'
                '.prf'         = 'application/pics-rules'
                '.prm'         = 'application/octet-stream'
                '.prx'         = 'application/octet-stream'
                '.ps'          = 'application/postscript'
                '.psd'         = 'application/octet-stream'
                '.psm'         = 'application/octet-stream'
                '.psp'         = 'application/octet-stream'
                '.pub'         = 'application/x-mspublisher'
                '.qt'          = 'video/quicktime'
                '.qtl'         = 'application/x-quicktimeplayer'
                '.qxd'         = 'application/octet-stream'
                '.ra'          = 'audio/x-pn-realaudio'
                '.ram'         = 'audio/x-pn-realaudio'
                '.rar'         = 'application/octet-stream'
                '.ras'         = 'image/x-cmu-raster'
                '.rf'          = 'image/vnd.rn-realflash'
                '.rgb'         = 'image/x-rgb'
                '.rm'          = 'application/vnd.rn-realmedia'
                '.rmi'         = 'audio/mid'
                '.roff'        = 'application/x-troff'
                '.rpm'         = 'audio/x-pn-realaudio-plugin'
                '.rtf'         = 'application/rtf'
                '.rtx'         = 'text/richtext'
                '.scd'         = 'application/x-msschedule'
                '.sct'         = 'text/scriptlet'
                '.sea'         = 'application/octet-stream'
                '.setpay'      = 'application/set-payment-initiation'
                '.setreg'      = 'application/set-registration-initiation'
                '.sgml'        = 'text/sgml'
                '.sh'          = 'application/x-sh'
                '.shar'        = 'application/x-shar'
                '.sit'         = 'application/x-stuffit'
                '.sldm'        = 'application/vnd.ms-powerpoint.slide.macroEnabled.12'
                '.sldx'        = 'application/vnd.openxmlformats-officedocument.presentationml.slide'
                '.smd'         = 'audio/x-smd'
                '.smi'         = 'application/octet-stream'
                '.smx'         = 'audio/x-smd'
                '.smz'         = 'audio/x-smd'
                '.snd'         = 'audio/basic'
                '.snp'         = 'application/octet-stream'
                '.spc'         = 'application/x-pkcs7-certificates'
                '.spl'         = 'application/futuresplash'
                '.src'         = 'application/x-wais-source'
                '.ssm'         = 'application/streamingmedia'
                '.sst'         = 'application/vnd.ms-pki.certstore'
                '.stl'         = 'application/vnd.ms-pki.stl'
                '.sv4cpio'     = 'application/x-sv4cpio'
                '.sv4crc'      = 'application/x-sv4crc'
                '.swf'         = 'application/x-shockwave-flash'
                '.t'           = 'application/x-troff'
                '.tar'         = 'application/x-tar'
                '.tcl'         = 'application/x-tcl'
                '.tex'         = 'application/x-tex'
                '.texi'        = 'application/x-texinfo'
                '.texinfo'     = 'application/x-texinfo'
                '.tgz'         = 'application/x-compressed'
                '.thmx'        = 'application/vnd.ms-officetheme'
                '.thn'         = 'application/octet-stream'
                '.tif'         = 'image/tiff'
                '.tiff'        = 'image/tiff'
                '.toc'         = 'application/octet-stream'
                '.tr'          = 'application/x-troff'
                '.trm'         = 'application/x-msterminal'
                '.tsv'         = 'text/tab-separated-values'
                '.ttf'         = 'application/octet-stream'
                '.txt'         = 'text/plain'
                '.u32'         = 'application/octet-stream'
                '.uls'         = 'text/iuls'
                '.ustar'       = 'application/x-ustar'
                '.vbs'         = 'text/vbscript'
                '.vcf'         = 'text/x-vcard'
                '.vcs'         = 'text/plain'
                '.vdx'         = 'application/vnd.ms-visio.viewer'
                '.vml'         = 'text/xml'
                '.vsd'         = 'application/vnd.visio'
                '.vss'         = 'application/vnd.visio'
                '.vst'         = 'application/vnd.visio'
                '.vsto'        = 'application/x-ms-vsto'
                '.vsw'         = 'application/vnd.visio'
                '.vsx'         = 'application/vnd.visio'
                '.vtx'         = 'application/vnd.visio'
                '.wav'         = 'audio/wav'
                '.wax'         = 'audio/x-ms-wax'
                '.wbmp'        = 'image/vnd.wap.wbmp'
                '.wcm'         = 'application/vnd.ms-works'
                '.wdb'         = 'application/vnd.ms-works'
                '.wks'         = 'application/vnd.ms-works'
                '.wm'          = 'video/x-ms-wm'
                '.wma'         = 'audio/x-ms-wma'
                '.wmd'         = 'application/x-ms-wmd'
                '.wmf'         = 'application/x-msmetafile'
                '.wml'         = 'text/vnd.wap.wml'
                '.wmlc'        = 'application/vnd.wap.wmlc'
                '.wmls'        = 'text/vnd.wap.wmlscript'
                '.wmlsc'       = 'application/vnd.wap.wmlscriptc'
                '.wmp'         = 'video/x-ms-wmp'
                '.wmv'         = 'video/x-ms-wmv'
                '.wmx'         = 'video/x-ms-wmx'
                '.wmz'         = 'application/x-ms-wmz'
                '.wps'         = 'application/vnd.ms-works'
                '.wri'         = 'application/x-mswrite'
                '.wrl'         = 'x-world/x-vrml'
                '.wrz'         = 'x-world/x-vrml'
                '.wsdl'        = 'text/xml'
                '.wvx'         = 'video/x-ms-wvx'
                '.x'           = 'application/directx'
                '.xaf'         = 'x-world/x-vrml'
                '.xaml'        = 'application/xaml+xml'
                '.xap'         = 'application/x-silverlight-app'
                '.xbap'        = 'application/x-ms-xbap'
                '.xbm'         = 'image/x-xbitmap'
                '.xdr'         = 'text/plain'
                '.xla'         = 'application/vnd.ms-excel'
                '.xlam'        = 'application/vnd.ms-excel.addin.macroEnabled.12'
                '.xlc'         = 'application/vnd.ms-excel'
                '.xlm'         = 'application/vnd.ms-excel'
                '.xls'         = 'application/vnd.ms-excel'
                '.xlsb'        = 'application/vnd.ms-excel.sheet.binary.macroEnabled.12'
                '.xlsm'        = 'application/vnd.ms-excel.sheet.macroEnabled.12'
                '.xlsx'        = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
                '.xlt'         = 'application/vnd.ms-excel'
                '.xltm'        = 'application/vnd.ms-excel.template.macroEnabled.12'
                '.xltx'        = 'application/vnd.openxmlformats-officedocument.spreadsheetml.template'
                '.xlw'         = 'application/vnd.ms-excel'
                '.xml'         = 'text/xml'
                '.xof'         = 'x-world/x-vrml'
                '.xpm'         = 'image/x-xpixmap'
                '.xps'         = 'application/vnd.ms-xpsdocument'
                '.xsd'         = 'text/xml'
                '.xsf'         = 'text/xml'
                '.xsl'         = 'text/xml'
                '.xslt'        = 'text/xml'
                '.xsn'         = 'application/octet-stream'
                '.xtp'         = 'application/octet-stream'
                '.xwd'         = 'image/x-xwindowdump'
                '.z'           = 'application/x-compress'
                '.zip'         = 'application/x-zip-compressed'
            }

            $extension = [System.IO.Path]::GetExtension($FileName)
            $contentType = $mimeMappings[$extension]

            if ([string]::IsNullOrEmpty($contentType))
            {
                return New-Object System.Net.Mime.ContentType
            }
            else
            {
                return New-Object System.Net.Mime.ContentType($contentType)
            }
        }

        try
        {
            $_smtpClient = New-Object Net.Mail.SmtpClient

            $_smtpClient.Host = $SmtpServer
            $_smtpClient.Port = $Port
            $_smtpClient.EnableSsl = $UseSsl

            if ($null -ne $Credential)
            {
                # In PowerShell 2.0, assigning the results of GetNetworkCredential() to the SMTP client sometimes fails (with gmail, in testing), but
                # building a new NetworkCredential object containing only the UserName and Password works okay.

                $_tempCred = $Credential.GetNetworkCredential()
                $_smtpClient.Credentials = New-Object Net.NetworkCredential($Credential.UserName, $_tempCred.Password)
            }
            else
            {
                $_smtpClient.UseDefaultCredentials = $true
            }

            $_message = New-Object Net.Mail.MailMessage

            $_message.From = $From
            $_message.Subject = $Subject

            if ($BodyAsHtml)
            {
                $_bodyPart = [Net.Mail.AlternateView]::CreateAlternateViewFromString($Body, 'text/html')
            }
            else
            {
                $_bodyPart = [Net.Mail.AlternateView]::CreateAlternateViewFromString($Body, 'text/plain')
            }

            $_message.AlternateViews.Add($_bodyPart)

            if ($PSBoundParameters.ContainsKey('DeliveryNotificationOption')) { $_message.DeliveryNotificationOptions = $DeliveryNotificationOption }
            if ($PSBoundParameters.ContainsKey('Priority')) { $_message.Priority = $Priority }

            foreach ($_address in $To)
            {
                if (-not $_message.To.Contains($_address)) { $_message.To.Add($_address) }
            }

            if ($null -ne $Cc)
            {
                foreach ($_address in $Cc)
                {
                    if (-not $_message.CC.Contains($_address)) { $_message.CC.Add($_address) }
                }
            }

            if ($null -ne $Bcc)
            {
                foreach ($_address in $Bcc)
                {
                    if (-not $_message.Bcc.Contains($_address)) { $_message.Bcc.Add($_address) }
                }
            }
        }
        catch
        {
            $_message.Dispose()
            throw
        }

        if ($PSBoundParameters.ContainsKey('InlineAttachments'))
        {
            foreach ($_entry in $InlineAttachments.GetEnumerator())
            {
                $_file = $_entry.Value.ToString()

                if ([string]::IsNullOrEmpty($_file))
                {
                    $_message.Dispose()
                    throw "Send-MailMessage: Values in the InlineAttachments table cannot be null."
                }

                try
                {
                    $_contentType = FileNameToContentType -FileName $_file
                    $_attachment = New-Object Net.Mail.LinkedResource($_file, $_contentType)
                    $_attachment.ContentId = $_entry.Key

                    $_bodyPart.LinkedResources.Add($_attachment)
                }
                catch
                {
                    $_message.Dispose()
                    throw
                }
            }
        }
    }

    process
    {
        if ($null -ne $Attachments)
        {
            foreach ($_file in $Attachments)
            {
                try
                {
                    $_contentType = FileNameToContentType -FileName $_file
                    $_message.Attachments.Add((New-Object Net.Mail.Attachment($_file, $_contentType)))
                }
                catch
                {
                    $_message.Dispose()
                    throw
                }
            }
        }
    }

    end
    {
        try
        {
            $_smtpClient.Send($_message)
        }
        catch
        {
            throw
        }
        finally
        {
            $_message.Dispose()
        }
    }

} # function Send-MailMessage