2026-03-29

Convert-Zo3MimeData.ps1

 <#

簡易MIMEメール作成スクリプト


cURLで送信する際に利用する。

検証環境やクローズド環境で使用して下さい。

 

MIT License

Copyright (c) 2026  yoshio (zo3.org)

Permission is hereby granted, free of charge, to any person obtaining a copy

of this software and associated documentation files (the "Software"), to deal

in the Software without restriction...

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND...

#>


param(

    [string]$From,

    [string[]]$To,

    [string]$Subject,

    [string]$Body,

    [string[]]$Attachments = @(),

    [string]$OutFile = "mail.eml"

)


# ===== 基本 =====

$CRLF = "`r`n"


# ===== RFC2047 Subject =====

function Encode-Subject( $string ) {

    $bytes = [System.Text.Encoding]::UTF8.GetBytes( $string )

    $b64data = [Convert]::ToBase64String( $bytes )

    return "=?UTF-8?B?${b64data}?="

}


# ===== Base64(76文字折返し)=====

function To-Base64Lines( $bytes ) {

    $b64data = [Convert]::ToBase64String( $bytes )

    return ( ${b64data} -split "(.{1,76})" | Where-Object { $_ }) -join $CRLF

}


# ===== RFC2231 filename* =====

function Encode-RFC2231( $string ) {

    $bytes = [System.Text.Encoding]::UTF8.GetBytes( $string )

    $enc = ""

    foreach ( $byte in $bytes ) {

        if (

            ( $byte -ge 0x30 -and $byte -le 0x39 ) -or

            ( $byte -ge 0x41 -and $byte -le 0x5A ) -or

            ( $byte -ge 0x61 -and $byte -le 0x7A ) -or

            $byte -in 0x2D, 0x2E, 0x5F, 0x7E

        ) {

            $enc += [char]$byte

        }

        else {

            $enc += "%" + $byte.ToString( "X2" )

        }

    }

    return "UTF-8''$enc"

}


# ===== Date / Message-ID =====

$now = Get-Date

$dateStr = $now.ToString("ddd, dd MMM yyyy HH:mm:ss K", [System.Globalization.CultureInfo]::InvariantCulture)


$domain = ($From -split "@")[-1]

$msgid = "<" + [guid]::NewGuid().ToString() + "@$domain>"


# ===== boundary =====

$boundary = "----=_Boundary_" + [guid]::NewGuid().ToString("N")


# ===== ヘッダ =====

$headers = @()

$headers += "Date: $dateStr"

$headers += "Message-ID: $msgid"

$headers += "From: $From"

$headers += "To: " + ($To -join ", ")

$headers += "Subject: " + ( Encode-Subject $Subject )

$headers += "MIME-Version: 1.0"


if ($Attachments.Count -gt 0) {

    $headers += "Content-Type: multipart/mixed; boundary=""$boundary"""

}

else {

    $headers += "Content-Type: text/plain; charset=UTF-8"

    $headers += "Content-Transfer-Encoding: 8bit"

}


# ===== 本文 =====

$bodyLines = @()


if ( $Attachments.Count -gt 0 ) {


    # 本文パート

    $bodyLines += "--$boundary"

    $bodyLines += "Content-Type: text/plain; charset=UTF-8"

    $bodyLines += "Content-Transfer-Encoding: 8bit"

    $bodyLines += ""

    $bodyLines += $Body


    # 添付

    foreach ( $file in $Attachments ) {


        $bytes = [System.IO.File]::ReadAllBytes( $file )

        $name = [System.IO.Path]::GetFileName( $file )


        # ASCIIフォールバック

        $nameAscii = $name -replace '[^\x20-\x7E]', '_'


        # RFC2231

        $name2231 = Encode-RFC2231 $name


        $bodyLines += ""

        $bodyLines += "--$boundary"

        $bodyLines += "Content-Type: application/octet-stream; name=""$nameAscii"""

        $bodyLines += "Content-Transfer-Encoding: base64"

        $bodyLines += "Content-Disposition: attachment; filename=""$nameAscii""; filename*=$name2231"

        $bodyLines += ""

        $bodyLines += (To-Base64Lines $bytes)

    }

    # 終端

    $bodyLines += ""

    $bodyLines += "--$boundary--"


}

else {

    $bodyLines += $Body

}


# ===== 結合 =====

$all = ( $headers -join $CRLF ) + $CRLF + $CRLF + ( $bodyLines -join $CRLF )


# ===== 出力(CRLF維持)=====

[System.IO.File]::WriteAllText( $OutFile , $all , [System.Text.Encoding]::ASCII)