|
- $ErrorActionPreference = 'Stop'
-
- function Get-MailContext {
- param([string]$ConfigPath = (Join-Path (Split-Path -Parent $PSScriptRoot) 'config.ini'))
-
- $settings = @{}
- foreach ($line in Get-Content -LiteralPath $ConfigPath -Encoding UTF8) {
- $trimmed = $line.Trim()
- if (-not $trimmed -or $trimmed.StartsWith('#') -or $trimmed.StartsWith(';')) { continue }
- $parts = $trimmed.Split('=', 2)
- if ($parts.Count -ne 2) { throw "Invalid configuration line in $ConfigPath" }
- $name = $parts[0].Trim().ToUpperInvariant()
- if ($settings.ContainsKey($name)) { throw "Duplicate setting: $name" }
- $settings[$name] = $parts[1].Trim()
- }
- foreach ($name in @('DB_PATH', 'DB_PROVIDER', 'DB_BITNESS', 'SMTP_SECRET_ID', 'SMTP_USER')) {
- if (-not $settings.ContainsKey($name) -or -not $settings[$name]) {
- throw "Missing setting: $name"
- }
- }
- if ($settings['DB_PROVIDER'] -notin @('Microsoft.Jet.OLEDB.4.0',
- 'Microsoft.ACE.OLEDB.12.0', 'Microsoft.ACE.OLEDB.16.0')) {
- throw 'Unsupported DB_PROVIDER'
- }
- $actualBitness = if ([IntPtr]::Size -eq 4) { 'x86' } else { 'x64' }
- if ($settings['DB_BITNESS'] -ne $actualBitness) {
- throw "Run this setup under $($settings['DB_BITNESS']) PowerShell"
- }
- $databasePath = $settings['DB_PATH']
- if ($databasePath.StartsWith('\\')) { throw 'DB_PATH must be on a local drive' }
- if (-not [System.IO.Path]::IsPathRooted($databasePath)) {
- $databasePath = Join-Path (Split-Path -Parent $PSScriptRoot) $databasePath
- }
- $databasePath = [System.IO.Path]::GetFullPath($databasePath)
- if ([System.IO.Path]::GetExtension($databasePath) -ne '.mdb') { throw 'DB_PATH must end in .mdb' }
- if (-not (Test-Path -LiteralPath $databasePath -PathType Leaf)) {
- throw "MDB not found: $databasePath"
- }
- return @{ Settings = $settings; DatabasePath = $databasePath }
- }
-
- function Open-MailDatabase {
- param($Context)
- $builder = New-Object System.Data.OleDb.OleDbConnectionStringBuilder
- $builder.Provider = $Context.Settings['DB_PROVIDER']
- $builder['Data Source'] = $Context.DatabasePath
- $connection = New-Object System.Data.OleDb.OleDbConnection($builder.ConnectionString)
- $connection.Open()
- return $connection
- }
-
- function Save-SmtpPassword {
- param($Context, [System.Security.SecureString]$Password)
- $encrypted = ConvertFrom-SecureString -SecureString $Password
- $bytes = [System.Text.Encoding]::UTF8.GetBytes($encrypted)
- $connection = Open-MailDatabase $Context
- try {
- $check = $connection.CreateCommand()
- $check.CommandText = 'SELECT COUNT(*) FROM [ProtectedSecret] WHERE [Id]=?'
- $null = $check.Parameters.Add('id', [System.Data.OleDb.OleDbType]::VarWChar, 100)
- $check.Parameters[0].Value = $Context.Settings['SMTP_SECRET_ID']
- $exists = [int]$check.ExecuteScalar() -gt 0
- if ($exists) {
- $command = $connection.CreateCommand()
- $command.CommandText = 'UPDATE [ProtectedSecret] SET [DpapiCiphertext]=?, [RotatedAtUtc]=? WHERE [Id]=?'
- $null = $command.Parameters.Add('ciphertext', [System.Data.OleDb.OleDbType]::LongVarBinary, $bytes.Length)
- $command.Parameters[0].Value = $bytes
- $null = $command.Parameters.Add('rotated', [System.Data.OleDb.OleDbType]::Date)
- $command.Parameters[1].Value = [DateTime]::UtcNow
- $null = $command.Parameters.Add('id', [System.Data.OleDb.OleDbType]::VarWChar, 100)
- $command.Parameters[2].Value = $Context.Settings['SMTP_SECRET_ID']
- }
- else {
- $command = $connection.CreateCommand()
- $command.CommandText = 'INSERT INTO [ProtectedSecret] ([Id],[Purpose],[DpapiCiphertext],[CreatedAtUtc]) VALUES (?,?,?,?)'
- $null = $command.Parameters.Add('id', [System.Data.OleDb.OleDbType]::VarWChar, 100)
- $command.Parameters[0].Value = $Context.Settings['SMTP_SECRET_ID']
- $null = $command.Parameters.Add('purpose', [System.Data.OleDb.OleDbType]::VarWChar, 50)
- $command.Parameters[1].Value = 'smtp_password'
- $null = $command.Parameters.Add('ciphertext', [System.Data.OleDb.OleDbType]::LongVarBinary, $bytes.Length)
- $command.Parameters[2].Value = $bytes
- $null = $command.Parameters.Add('created', [System.Data.OleDb.OleDbType]::Date)
- $command.Parameters[3].Value = [DateTime]::UtcNow
- }
- $null = $command.ExecuteNonQuery()
- }
- finally {
- $connection.Dispose()
- [Array]::Clear($bytes, 0, $bytes.Length)
- }
- }
-
- function Load-SmtpPassword {
- param($Context)
- $connection = Open-MailDatabase $Context
- try {
- $command = $connection.CreateCommand()
- $command.CommandText = 'SELECT [DpapiCiphertext] FROM [ProtectedSecret] WHERE [Id]=? AND [Purpose]=?'
- $null = $command.Parameters.Add('id', [System.Data.OleDb.OleDbType]::VarWChar, 100)
- $command.Parameters[0].Value = $Context.Settings['SMTP_SECRET_ID']
- $null = $command.Parameters.Add('purpose', [System.Data.OleDb.OleDbType]::VarWChar, 50)
- $command.Parameters[1].Value = 'smtp_password'
- $bytes = $command.ExecuteScalar()
- if ($null -eq $bytes -or $bytes -is [DBNull]) { throw 'SMTP password is not configured' }
- $encrypted = [System.Text.Encoding]::UTF8.GetString($bytes)
- return ConvertTo-SecureString -String $encrypted
- }
- finally {
- $connection.Dispose()
- }
- }
|