Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
187 changes: 186 additions & 1 deletion Start-ExchangeRecipientAdminCenter.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ try {
"$(Get-Date -Format s) Powershell webserver started."
$WEBLOG = "$(Get-Date -Format s) Powershell webserver started.`n"
while ($LISTENER.IsListening) {
try {
# analyze incoming request
$CONTEXT = $LISTENER.GetContext()
$REQUEST = $CONTEXT.Request
Expand Down Expand Up @@ -139,7 +140,182 @@ try {
break
}

"GET /distributiongroups" {
"GET /editremotemailbox" {
# View / edit a single Remote Mailbox's attributes

$Table = @{}
if ($REQUEST.Url.Query) {
foreach ($Item in [URI]::UnescapeDataString(($REQUEST.Url.Query.Replace("?", ""))).Split("&")) {
$Table.Add($Item.Split("=")[0], $Item.Split("=")[1])
}
}

$Identity = $Table['id']
$MailboxDisabled = $false

# Apply a requested change, if any, before re-reading the mailbox
if ($Table.ContainsKey('action')) {
try {
switch ($Table['action']) {
"addalias" {
$NewAlias = "$($Table['alias_local'])@$($Table['alias_accepteddomain'])"
Set-RemoteMailbox -Identity $Identity -EmailAddresses @{Add = $NewAlias }
$HTML_RESULT = $HTML_SUCCESS.Replace("{result}", "Added alias $NewAlias")
break
}
"removealias" {
Set-RemoteMailbox -Identity $Identity -EmailAddresses @{Remove = $Table['alias'] }
$HTML_RESULT = $HTML_SUCCESS.Replace("{result}", "Removed alias $($Table['alias'])")
break
}
"togglehidden" {
$NewHidden = $Table['hidden'] -eq 'true'
Set-RemoteMailbox -Identity $Identity -HiddenFromAddressListsEnabled $NewHidden
$HTML_RESULT = $HTML_SUCCESS.Replace("{result}", "Set 'Hidden from address lists' to $NewHidden")
break
}
"setrra" {
Set-RemoteMailbox -Identity $Identity -RemoteRoutingAddress $Table['newrra']
$HTML_RESULT = $HTML_SUCCESS.Replace("{result}", "Remote routing address updated to $($Table['newrra'])")
break
}
"disable" {
# Require the confirmation text to match the mailbox's actual
# PrimarySmtpAddress - checked server-side too, not just via the
# UI's type-to-confirm JS, since that's trivially bypassed.
$MailboxToDisable = Get-RemoteMailbox -Identity $Identity -ErrorAction SilentlyContinue
if (-not $MailboxToDisable) {
$HTML_RESULT = $HTML_WARN.Replace("{result}", "Remote mailbox '$($Identity)' not found")
}
elseif ($Table['confirmAddress'] -ne $MailboxToDisable.PrimarySmtpAddress) {
$HTML_RESULT = $HTML_WARN.Replace("{result}", "Confirmation text didn't match $($MailboxToDisable.PrimarySmtpAddress) - no changes were made")
}
else {
Disable-RemoteMailbox -Identity $Identity -Confirm:$false
$HTML_RESULT = $HTML_SUCCESS.Replace("{result}", "Remote Mailbox for $($MailboxToDisable.PrimarySmtpAddress) has been disabled")
$MailboxDisabled = $true
}
break
}
}
}
catch {
$HTML_RESULT = $HTML_WARN.Replace("{result}", $Error -join "<br />")
}
}

# A disabled mailbox no longer exists to render an edit page for -
# send the user back to the Remote Mailboxes list instead.
if ($MailboxDisabled) {
$HTMLROWS_USERS = ""
foreach ($Item in (Get-User -Filter "RecipientType -eq 'User' -and RecipientTypeDetails -ne 'DisabledUser'" | Where { $_.UserPrincipalName })) {
$HTMLROWS_USERS += "`n<option value=`"$($Item.UserPrincipalName)`">$($Item.UserPrincipalName)</option>"
}

$HTMLROWS_AD = ""
foreach ($Item in (Get-AcceptedDomain)) {
if ($Item.Default) {
$HTMLROWS_AD += "`n<option selected value=`"$($Item.Name)`">$($Item.DomainName)</option>"
}
else {
$HTMLROWS_AD += "`n<option value=`"$($Item.Name)`">$($Item.DomainName)</option>"
}
}

$HTMLROWS_RRA = ""
foreach ($Item in (Get-AcceptedDomain)) {
if ($Item.DomainName -like "*.mail.onmicrosoft.com") {
$HTMLROWS_RRA += "`n<option selected value=`"$($Item.Name)`">$($Item.DomainName)</option>"
}
else {
$HTMLROWS_RRA += "`n<option value=`"$($Item.Name)`">$($Item.DomainName)</option>"
}
}

$HTMLROWS_MBX = ""
foreach ($Item in (Get-RemoteMailbox | Select DisplayName, PrimarySMTPAddress, RecipientTypeDetails, WhenChanged)) {
$HTMLROWS_MBX += "
<tr>
<th scope=`"row`">
<a href=`"/editremotemailbox?id=$($Item.PrimarySMTPAddress)`">$($Item.DisplayName)</a></th>
<td>$($Item.PrimarySMTPAddress)</td>
<td>$($Item.RecipientTypeDetails)</td>
<td>$($Item.WhenChanged)</td>
</tr>";
}

$HTMLRESPONSE = (Get-Content -Path "$($BASEDIR)\remotemailboxes.html").Replace("<!-- {row_mbx} -->", $HTMLROWS_MBX).Replace("<!-- {row_ad} -->", $HTMLROWS_AD).Replace("<!-- {row_user} -->", $HTMLROWS_USERS).Replace("<!-- {row_rra} -->", $HTMLROWS_RRA).Replace("<!-- {result} -->", $HTML_RESULT)
break
}

# Look up the mailbox to display
$Mailbox = Get-RemoteMailbox -Identity $Identity -ErrorAction SilentlyContinue

if (-not $Mailbox) {
$RESPONSE.StatusCode = 404
$HTMLRESPONSE = "<!doctype html><html><body>Remote mailbox '$($Identity)' not found</body></html>"
break
}

# Prepare accepted domain list, used for the "add alias" domain picker
$HTMLROWS_AD = ""
foreach ($Item in (Get-AcceptedDomain)) {
$HTMLROWS_AD += "`n<option value=`"$($Item.Name)`">$($Item.DomainName)</option>"
}

# Prepare proxy address (EmailAddresses) rows, each with a remove button
$HTMLROWS_PROXY = ""
foreach ($Addr in $Mailbox.EmailAddresses) {
$AddrString = $Addr.ToString()
$IsPrimary = $AddrString.StartsWith("SMTP:")
if ($IsPrimary) {
$Badge = "<span class=`"badge text-bg-primary`">Primary</span>"
$RemoveBtn = ""
}
else {
$Badge = "<span class=`"badge text-bg-secondary`">Alias</span>"
$EncodedId = [URI]::EscapeDataString($Identity)
$EncodedAddr = [URI]::EscapeDataString($AddrString)
$RemoveBtn = "<a class=`"btn btn-sm btn-outline-danger`" href=`"/editremotemailbox?id=$($EncodedId)&action=removealias&alias=$($EncodedAddr)`" onclick=`"return confirm('Remove $($AddrString)?')`">Remove</a>"
}
$HTMLROWS_PROXY += "
<tr>
<td>$($AddrString)</td>
<td>$($Badge)</td>
<td>$($RemoveBtn)</td>
</tr>";
}

if ($Mailbox.HiddenFromAddressListsEnabled) {
$HiddenBadgeClass = "text-bg-warning"
$HiddenStatusText = "Hidden"
$HiddenToggleValue = "false"
$HiddenToggleLabel = "Unhide from GAL"
}
else {
$HiddenBadgeClass = "text-bg-success"
$HiddenStatusText = "Visible"
$HiddenToggleValue = "true"
$HiddenToggleLabel = "Hide from GAL"
}

# Create response and replace template placeholders
$HTMLRESPONSE = Get-Content -Path "$($BASEDIR)\editremotemailbox.html"
$HTMLRESPONSE = $HTMLRESPONSE.Replace("{display_name}", $Mailbox.Name)
$HTMLRESPONSE = $HTMLRESPONSE.Replace("{primarysmtpaddress}", $Mailbox.PrimarySmtpAddress)
$HTMLRESPONSE = $HTMLRESPONSE.Replace("{remoteroutingaddress}", $Mailbox.RemoteRoutingAddress)
$HTMLRESPONSE = $HTMLRESPONSE.Replace("{id}", [URI]::EscapeDataString($Identity))
$HTMLRESPONSE = $HTMLRESPONSE.Replace("{hidden_badge_class}", $HiddenBadgeClass)
$HTMLRESPONSE = $HTMLRESPONSE.Replace("{hidden_status_text}", $HiddenStatusText)
$HTMLRESPONSE = $HTMLRESPONSE.Replace("{hidden_toggle_value}", $HiddenToggleValue)
$HTMLRESPONSE = $HTMLRESPONSE.Replace("{hidden_toggle_label}", $HiddenToggleLabel)
$HTMLRESPONSE = $HTMLRESPONSE.Replace("<!-- {row_proxy} -->", $HTMLROWS_PROXY)
$HTMLRESPONSE = $HTMLRESPONSE.Replace("<!-- {row_ad} -->", $HTMLROWS_AD)
$HTMLRESPONSE = $HTMLRESPONSE.Replace("<!-- {result} -->", $HTML_RESULT)
break
}

"GET /distributiongroups" {
# Distribution Groups Section

# Prepare Distibution Group lists split into tabs
Expand Down Expand Up @@ -334,6 +510,15 @@ try {
"$(Get-Date -Format s) Stopping powershell webserver..."
break;
}
}
catch {
# A single request failing (e.g. client closed the connection before the
# response finished writing - "network name is no longer available") should
# not take down the whole webserver. Log it and keep listening.
"$(Get-Date -Format s) Request error: $($_.Exception.Message)"
$WEBLOG += "$(Get-Date -Format s) Request error: $($_.Exception.Message)`n"
try { $RESPONSE.Close() } catch { }
}
}
}
finally {
Expand Down
Loading