468 lines
14 KiB
PowerShell
468 lines
14 KiB
PowerShell
# Check to see if we're interactive or not.... this is a dirty, dirty
|
|
# hack.
|
|
$interactive = $true
|
|
$cmdLine = [System.Environment]::CommandLine
|
|
if ($cmdLine.Contains("powershell.exe") -and ($cmdLine.Contains(".ps1") -and !$cmdLine.Contains("-noexit")))
|
|
{
|
|
# We're running non-interactively, don't do any of this stuff.
|
|
$interactive = $false
|
|
}
|
|
|
|
if ($interactive)
|
|
{
|
|
# Make MSYS based things work correctly (most importantly, msysgit)
|
|
#
|
|
$env:TERM='msys'
|
|
|
|
$profileDir = split-path -parent $Profile
|
|
|
|
# Ensure HOME is set properly
|
|
#
|
|
$env:Home = [Environment]::GetFolderPath("Personal")
|
|
set-variable -name HOME -value (resolve-path $env:Home) -force
|
|
(get-psprovider FileSystem).Home = $HOME
|
|
|
|
# Add the right things to the path
|
|
#
|
|
$env:Path = "$env:windir\Microsoft.NET\Framework64;" + $env:Path
|
|
$env:Path = "c:\debuggers;c:\tools\x86;c:\tools\x86\bin;c:\emacs\bin" + $env:Path
|
|
|
|
# Fun times with diffs and the like.
|
|
#
|
|
$env:SDPWDIFF = 'odd.exe'
|
|
$env:SDVDIFF = 'odd.exe -l!'
|
|
$env:SDVCDIFF = 'odd.exe -ld'
|
|
}
|
|
|
|
# Based on http://winterdom.com/2008/08/mypowershellprompt
|
|
function shorten-path([string] $path)
|
|
{
|
|
$loc = $path.Replace($HOME, '~')
|
|
# remove prefix for UNC paths
|
|
$loc = $loc -replace '^[^:]+::', ''
|
|
# make path shorter like tabs in Vim,
|
|
# handle paths starting with \\ and . correctly
|
|
return ($loc -replace '\\(\.?)([^\\]{3})[^\\]*(?=\\)','\$1$2')
|
|
}
|
|
|
|
function prompt
|
|
{
|
|
$ok = $?
|
|
|
|
if ($global:SolarizedColors)
|
|
{
|
|
# Our "theme", as it were. Note that we assume the use of the
|
|
# solarized colors.
|
|
#
|
|
$cdelim = ConvertFrom-SolarizedColor "base1"
|
|
$chost = ConvertFrom-SolarizedColor "green"
|
|
$cloc = $csym = ConvertFrom-SolarizedColor "base0"
|
|
if (-not $ok) { $csym = ConvertFrom-SolarizedColor "red" }
|
|
}
|
|
else
|
|
{
|
|
$cdelim = [ConsoleColor]::Gray
|
|
$chost = [ConsoleColor]::Green
|
|
$cloc = $csym = [ConsoleColor]::Gray
|
|
if (-not $ok) { $csym = [ConsoleColor]::Red; }
|
|
}
|
|
|
|
write-host "$([char]0x0A7) " -n -f $csym
|
|
write-host ([net.dns]::GetHostName()) -n -f $chost
|
|
write-host ' {' -n -f $cdelim
|
|
write-host (shorten-path (pwd).Path) -n -f $cloc
|
|
write-host '}' -n -f $cdelim
|
|
|
|
return ' '
|
|
}
|
|
|
|
|
|
function Get-APFile($Path, $Cockpit="BLUINFDPXYIH802:80")
|
|
{
|
|
$localFile = join-path $pwd (split-path -leaf $Path)
|
|
$uri = "http://$Cockpit/files?cmd=get&path=$Path"
|
|
Write-Host "Getting $Path to $localFile via $uri"
|
|
(New-Object System.Net.Webclient).DownloadFile($uri, $localFile)
|
|
}
|
|
|
|
function Get-Url($Url)
|
|
{
|
|
(New-Object System.Net.Webclient).DownloadString($Url)
|
|
}
|
|
|
|
function ConvertFrom-JWT($text)
|
|
{
|
|
$text = $text.Trim()
|
|
$part = $text.Split('.')[1]
|
|
while($part.Length % 4) {
|
|
$part = $part + '='
|
|
}
|
|
|
|
$json = [System.Text.Encoding]::UTF8.GetString([Convert]::FromBase64String($part))
|
|
return ConvertFrom-Json $json
|
|
}
|
|
|
|
function ConvertFrom-KeyType($data)
|
|
{
|
|
switch([BitConverter]::ToInt16($data,0)) {
|
|
1 { "SessionRoot"; break; }
|
|
2 { "SessionRootEscrow_AES"; break; }
|
|
3 { "K_R_G"; break; }
|
|
4 { "Live_RSA"; break; }
|
|
5 { "ContentKey"; break; }
|
|
6 { "DevContentKeyEscrow_RSA"; break; }
|
|
7 { "ProdContentKeyEscrow_RSA"; break; }
|
|
8 { "XboxAutoVaultSign_RSA"; break; }
|
|
9 { "EscrowedDevContentKeyEKB_RSA"; break; }
|
|
10 { "EscrowedProdContentKeyEKB_RSA"; break; }
|
|
11 { "DevDurangoGlobalEscrow_RSA"; break; }
|
|
12 { "ProdDurangoGlobalEscrow_RSA"; break; }
|
|
13 { "DevKitConversionCert_RSA"; break; }
|
|
default { "Unknown ($_)"; break; }
|
|
}
|
|
}
|
|
|
|
function ConvertFrom-EKB($text)
|
|
{
|
|
$ekb = @{}
|
|
|
|
$bytes = [Convert]::FromBase64String($text)
|
|
|
|
$stream = New-Object System.IO.MemoryStream
|
|
$stream.Write($bytes,0,$bytes.Length)
|
|
$stream.Position = 0
|
|
|
|
$reader = New-Object System.IO.BinaryReader($stream)
|
|
|
|
$stream.Seek(8, 'Current') | out-null
|
|
while($stream.Position -ne $stream.Length) {
|
|
$type = $reader.ReadInt16()
|
|
$length = $reader.ReadInt32()
|
|
$data = $reader.ReadBytes($length)
|
|
|
|
switch($type){
|
|
1 {
|
|
$ekb.EscrowedKey = ConvertFrom-KeyType($data)
|
|
break
|
|
}
|
|
2 {
|
|
$ekb.KeyID = [System.Text.Encoding]::UTF8.GetString($data)
|
|
if ($ekb.KeyID.Length -eq 32) {
|
|
$kid = $ekb.KeyID
|
|
$gah = @()
|
|
while ($kid.Length) {
|
|
$gah += @([Byte]::Parse($kid.Substring(0,2), "HexNumber"))
|
|
$kid = $kid.Substring(2)
|
|
}
|
|
$ekb.KeyIDGuid = [Guid][byte[]]($gah)
|
|
}
|
|
break
|
|
}
|
|
3 {
|
|
$ekb.EscrowingKeyType = ConvertFrom-KeyType($data)
|
|
break
|
|
}
|
|
4 {
|
|
$ekb.EscrowingKeyID = [System.Text.Encoding]::UTF8.GetString($data)
|
|
break
|
|
}
|
|
5 {
|
|
$ekb.EscrowMethod = switch([BitConverter]::ToInt16($data, 0)){
|
|
1 { "RSAEncrypt_OAEP_SHA256"; break; }
|
|
2 { "AESKeyWrap"; break; }
|
|
3 { "RSAEncrypt_PKCS1_SHA256"; break; }
|
|
default { "Unknown ($_)"; break; }
|
|
}
|
|
break
|
|
}
|
|
6 {
|
|
$ekb.CustomData = $data
|
|
break
|
|
}
|
|
7 {
|
|
$ekb.EscrowBlob = $data
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
return $ekb
|
|
}
|
|
|
|
function Wrap-Text ($txt)
|
|
{
|
|
$t = [string]$txt
|
|
while($t.Length -gt 77) {
|
|
$t.Substring(0, 77)
|
|
$t = $t.Substring(77)
|
|
}
|
|
$t
|
|
}
|
|
|
|
function ConvertFrom-Base64UTF8($base64) {
|
|
return [System.Text.Encoding]::UTF8.GetString([Convert]::FromBase64String($base64))
|
|
}
|
|
|
|
|
|
function Save-Change($ChangeNumber, $Description)
|
|
{
|
|
if (!$ChangeNumber) { $ChangeNumber = "default" }
|
|
if (!$Description) { $Description = "Change-$ChangeNumber" }
|
|
|
|
$PackPath = Resolve-Path "~\dpk"
|
|
if (!(Test-Path $PackPath))
|
|
{
|
|
mkdir $PackPath | out-null
|
|
}
|
|
|
|
# Get Unique number...
|
|
$Date = Get-Date
|
|
$Prefix = "$($Date.Year).$($Date.Month.ToString('00')).$($Date.Day.ToString('00'))"
|
|
|
|
$Index = "00"
|
|
$items = @(Get-ChildItem $PackPath -Filter "$Prefix*.dpk" | Sort-Object -Property FullName -Descending)
|
|
if ($items.Length -gt 0)
|
|
{
|
|
$idx = [int]($items[0].FullName.Split('.')[3]) + 1
|
|
$Index = $idx.ToString('00')
|
|
}
|
|
|
|
$PackFile = "$PackPath\$Prefix.$Index.$Description.dpk"
|
|
Write-Host "Packing change '$ChangeNumber' to '$PackFile'..."
|
|
sdp pack $PackFile -c $ChangeNumber
|
|
}
|
|
|
|
function Convert-HexNumberToGuid($hn)
|
|
{
|
|
$gah = @()
|
|
while ($hn.Length)
|
|
{
|
|
$gah += @([Byte]::Parse($hn.Substring(0,2), "HexNumber"))
|
|
$hn = $hn.Substring(2)
|
|
}
|
|
return [Guid][byte[]]($gah)
|
|
}
|
|
|
|
function global:bld { build $args }
|
|
function global:bz { build -PZM $args }
|
|
function global:bcz { build -cPZM $args }
|
|
|
|
function Get-TFSWorkspace(
|
|
[string]$path = ((pwd).Path)
|
|
)
|
|
{
|
|
[void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.TeamFoundation.Client")
|
|
[void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.TeamFoundation.VersionControl.Client")
|
|
[void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.TeamFoundation.WorkItemTracking.Client")
|
|
[void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.TeamFoundation.Build.Client")
|
|
|
|
function InitServerAndWorkspaceFromWSInfo( $wsInfo )
|
|
{
|
|
$tfs = New-Object Microsoft.TeamFoundation.Client.TfsTeamProjectCollection( $wsInfo.ServerUri )
|
|
$versionControlServer = $tfs.GetService([Microsoft.TeamFoundation.VersionControl.Client.VersionControlServer])
|
|
|
|
return @{
|
|
"BuildServer"= $tfs.GetService([Microsoft.TeamFoundation.Build.Client.IBuildServer]);
|
|
"VersionControl"=$versionControlServer;
|
|
"WorkItems"=$tfs.GetService([Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItemStore]);
|
|
"Workspace"=$versionControlServer.GetWorkspace($wsInfo);
|
|
}
|
|
}
|
|
|
|
# is there only 1 workspace in our cache file? If so, use that one regardless of the hint
|
|
$workspaceInfos = [Microsoft.TeamFoundation.VersionControl.Client.Workstation]::Current.GetAllLocalWorkspaceInfo()
|
|
if ($workspaceInfos.Length -eq 1)
|
|
{
|
|
InitServerAndWorkspaceFromWSInfo($workspaceInfos[0])
|
|
return
|
|
}
|
|
|
|
$current = $path
|
|
do
|
|
{
|
|
$workspaceInfos = [Microsoft.TeamFoundation.VersionControl.Client.Workstation]::Current.GetLocalWorkspaceInfoRecursively($current)
|
|
if ($workspaceInfos.Length -gt 1)
|
|
{
|
|
throw 'More than one workspace matches the workspace hint "{0}": {1}' -f
|
|
$current, [string]::join(', ', @($workspaceInfos | %{ $_.Name}))
|
|
}
|
|
|
|
$current = split-path -parent $current
|
|
} while (($workspaceInfos.Length -ne 1) -and $current)
|
|
|
|
if (-not $workspaceInfos)
|
|
{
|
|
throw "Could not figure out a workspace based on $path"
|
|
}
|
|
|
|
return InitServerAndWorkspaceFromWSInfo( $workspaceInfos[0] )
|
|
}
|
|
|
|
function Start-IIS(
|
|
[string]$Path = ".",
|
|
[string]$Port = "8080",
|
|
[string]$CLR,
|
|
[switch]$SysTray,
|
|
[string]$TraceLevel
|
|
)
|
|
{
|
|
$iis_args = @("/path:$Path")
|
|
if ($Port) { $iis_args += @("/port:$Port") }
|
|
if ($CLR) { $iis_args += @("/clr:$CLR") }
|
|
if ($SysTray) { $iis_args += @("/systray:true") }
|
|
if ($TraceLevel) { $iis_args += @("/trace:$TraceLevel") }
|
|
|
|
Start-Process -Wait -NoNewWindow -FilePath "${env:ProgramFiles}\IIS Express\iisexpress.exe" -ArgumentList $iis_args
|
|
}
|
|
|
|
|
|
function Find-CatalogProducts(
|
|
[string]$CatalogQuery,
|
|
[switch]$Raw,
|
|
[string]$Market='US',
|
|
[string]$Language='en-US')
|
|
{
|
|
$local:ErrorActionPreference = "Stop"
|
|
|
|
$hdr = @{ 'MS-CV'="LxFzVzL+JUG/kPoc.103"; }
|
|
$x = Invoke-WebRequest "https://displaycatalog.md.mp.microsoft.com/v6/products?rank=ProductSearchApps&query=$CatalogQuery&market=$Market&languages=$Language&fieldsTemplate=Full" -Headers $hdr
|
|
if ($Raw) {
|
|
return $x.Content
|
|
} else {
|
|
$y = convertfrom-json $x.Content
|
|
return $y.DisplayProductSearchResult.Products
|
|
}
|
|
}
|
|
|
|
|
|
function Get-CatalogProduct(
|
|
[string]$ProductId = $null,
|
|
[string]$PackageFamilyName = $null,
|
|
[string]$ContentId = $null,
|
|
[string]$LegacyPhoneProductId = $null,
|
|
[string]$LegacyDesktopProductId = $null,
|
|
[switch]$Raw,
|
|
[string]$Market='US',
|
|
[string]$Language='en-US')
|
|
{
|
|
$hdr = @{ 'MS-CV'="LxFzVzL+JUG/kPoc.99"; }
|
|
$local:ErrorActionPreference = "Stop"
|
|
|
|
if ($ProductId) {
|
|
$url = "https://displaycatalog.md.mp.microsoft.com/v6.0/products/$($ProductId)?fieldsTemplate=Full&market=$($Market)&languages=$($Language)"
|
|
} elseif ($PackageFamilyName) {
|
|
$url = "https://displaycatalog.md.mp.microsoft.com/v6.0/products?rank=PackageFamilyName&alternateId=$($PackageFamilyName)&market=$($Market)&languages=$($Language)&fieldsTemplate=Full"
|
|
} elseif ($LegacyPhoneProductId) {
|
|
$url = "https://displaycatalog.md.mp.microsoft.com/v6.0/products?rank=LegacyWindowsPhoneProductId&alternateId=$($LegacyPhoneProductId)&market=$($Market)&languages=$($Language)&fieldsTemplate=Full"
|
|
} elseif ($LegacyDesktopProductId) {
|
|
$url = "https://displaycatalog.md.mp.microsoft.com/v6.0/skus?rank=WuCategoryId&alternateId=$($LegacyDesktopProductId)&market=$($Market)&languages=$($Language)&fieldsTemplate=Full"
|
|
} else {
|
|
$url = "https://displaycatalog.md.mp.microsoft.com/v6.0/skus?rank=ContentId&alternateId=$($ContentId)&market=$($Market)&languages=$($Language)&fieldsTemplate=Full"
|
|
}
|
|
|
|
write-host $url
|
|
|
|
$x = Invoke-WebRequest $url -Headers $hdr
|
|
if ($Raw) {
|
|
return $x.Content
|
|
} else {
|
|
$y = convertfrom-json $x.Content
|
|
if ($y.Product) {
|
|
return $y.Product
|
|
} elseif ($y.DisplayProductSearchResult) {
|
|
return $y.DisplayProductSearchResult.Products[0]
|
|
} elseif ($y.DisplaySkuSearchResult) {
|
|
return $y.DisplaySkuSearchResult.Products[0]
|
|
}
|
|
}
|
|
}
|
|
|
|
function Get-MSATicket()
|
|
{
|
|
$local:ErrorActionPreference = "Stop"
|
|
|
|
# ALL OF THIS IS NECESSARY. (By trial and error.)
|
|
$req = [Windows.Security.Authentication.OnlineId.OnlineIdServiceTicketRequest,Windows.Security.Authentication.OnlineId,ContentType=WindowsRuntime]::new("www.microsoft.com", "mbi_ssl")
|
|
|
|
$x = [System.Collections.Generic.List[Windows.Security.Authentication.OnlineId.OnlineIdServiceTicketRequest]]::new()
|
|
$x.Add($req)
|
|
|
|
$authn = new-object "Windows.Security.Authentication.OnlineId.OnlineIdAuthenticator,Windows.Security.Authentication.OnlineId,ContentType=WindowsRuntime"
|
|
$authn.ApplicationId = "{d6d5a677-0872-4ab0-9442-bb792fce85c5}"
|
|
$ar = $authn.AuthenticateUserAsync($x, [Windows.Security.Authentication.OnlineId.CredentialPromptType]::DoNotPrompt)
|
|
while ($ar.Status -ne [Windows.Foundation.AsyncStatus]::Completed) { start-sleep -milliseconds 10 }
|
|
return $ar.GetResults().Tickets.Value
|
|
}
|
|
|
|
function Get-Entitlements(
|
|
$ProductTypes=@('Application','Durable','Consumable','UnmanagedConsumable'),
|
|
$ProductId=$null,
|
|
$SkuId=$null,
|
|
$Market='US'
|
|
)
|
|
{
|
|
$local:ErrorActionPreference = "Stop"
|
|
$ticket = Get-MSATicket
|
|
|
|
$req = @{
|
|
'beneficiaries' = @(
|
|
@{
|
|
'identityType' = 'msa';
|
|
'identityValue' = Get-MSATicket;
|
|
'localTicketReference' = 'yes';
|
|
}
|
|
);
|
|
'productTypes' = $ProductTypes;
|
|
'market' = $Market;
|
|
'validityType' = 'Valid';
|
|
}
|
|
|
|
if ($ProductId) {
|
|
$psid = @{ 'productId' = $ProductId; }
|
|
if ($SkuId) {
|
|
$psid.skuId = $SkuId
|
|
}
|
|
$req.productSkuIds = @( $psid )
|
|
}
|
|
|
|
#TODO: Make the pipeline work for you, man
|
|
|
|
$its = @()
|
|
$ct = $null
|
|
do {
|
|
if ($ct) {
|
|
$req.continuationToken = $ct
|
|
}
|
|
$rj = ConvertTo-Json $req
|
|
|
|
$result = Invoke-WebRequest -Uri "https://collections.md.mp.microsoft.com/v6.0/collections/query" -Body $rj -ContentType "application/json" -Method "POST"
|
|
|
|
$resp = ConvertFrom-Json $result.Content
|
|
$its = $its + $resp.items
|
|
$ct = $resp.continuationToken
|
|
} while($ct)
|
|
|
|
return $its
|
|
}
|
|
|
|
function Revoke-Order(
|
|
$OrderId,
|
|
$Market='US',
|
|
$Language='en-us'
|
|
)
|
|
{
|
|
$local:ErrorActionPreference = "Stop"
|
|
$ticket = Get-MSATicket
|
|
|
|
$req = @{
|
|
'clientContext' = @{
|
|
'client' = 'DotyPowershellExtravaganza'
|
|
};
|
|
'orderState' = 'Refunded';
|
|
}
|
|
|
|
$headers = @{ "Authorization"="WLID1.0=$ticket"; }
|
|
$body = ConvertTo-Json $req
|
|
$result = Invoke-WebRequest -Uri "https://purchase.md.mp.microsoft.com/v6.0/users/me/orders/$OrderId" -Body $body -ContentType "application/json" -Method "PUT" -Headers $headers
|
|
return ConvertFrom-Json $result.Content
|
|
}
|