2026-03-29

Convert-Zo3ToMail.ps1

 <#

簡易MIMEメール展開スクリプト


cURLで送信したEMLファイルを展開する。

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


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]$EmlFile,

    [string]$OutDir = ".\out"

)


$CRLF = "`r`n"


# 出力先

New-Item -ItemType Directory -Force -Path $OutDir | Out-Null


# 全文取得

$content = Get-Content -Raw -Path $EmlFile


# ヘッダとボディ分離

$parts = $content -split "$CRLF$CRLF", 2

$headers = $parts[0]

$body = $parts[1]


# boundary取得

$boundary = $null

if ( $headers -match 'boundary="([^"]+)"') {

    $boundary = $matches[1]

}


# RFC2231 decode

function Decode-RFC2231($s) {

    if ($s -match "UTF-8''(.+)") {

        $enc = $matches[1]

        $bytes = @()

        for ($i = 0; $i -lt $enc.Length; ) {

            if ($enc[$i] -eq '%') {

                $bytes += [Convert]::ToByte($enc.Substring($i + 1, 2), 16)

                $i += 3

            }

            else {

                $bytes += [byte][char]$enc[$i]

                $i++

            }

        }

        return [System.Text.Encoding]::UTF8.GetString($bytes)

    }

    return $s

}


# Base64 decode

function Decode-Base64($text) {

    $clean = ($text -replace '\s', '')

    return [Convert]::FromBase64String($clean)

}


if ( $null -eq $boundary ) {

    # 単一パート 本文のみ

    $textPath = Join-Path $OutDir "body.txt"

    Set-Content -Path $textPath -Value $body -Encoding UTF8

}

else {

    # マルチパート分解

    $sections = $body -split "--$boundary"


    foreach ($sec in $sections) {


        if ($sec -match "--\s*$") { continue }

        if ($sec.Trim() -eq "") { continue }


        $sp = $sec -split "$CRLF$CRLF", 2

        if ( $sp.Count -lt 2 ) { continue }


        $h = $sp[0]

        $b = $sp[1].Trim()


        # 添付判定

        if ( -not ( $h -match "Content-Disposition: attachment" ) ) {

            # 本文

            $textPath = Join-Path $OutDir "body.txt"

            Set-Content -Path $textPath -Value $b -Encoding UTF8

        }

        else {

            # filename取得(優先:filename*)

            $filename = "unknown.bin"


            if ($h -match "filename\*=(.+)") {

                $filename = Decode-RFC2231 $matches[1].Trim()

            }

            elseif ($h -match 'filename="([^"]+)"') {

                $filename = $matches[1]

            }


            # Base64デコード

            if ($h -match "base64") {

                $bytes = Decode-Base64 $b

                $outPath = Join-Path $OutDir $filename

                [System.IO.File]::WriteAllBytes($outPath, $bytes)

            }

        }

    }

}


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)