UMN-Github.psm1
### # Copyright 2017 University of Minnesota, Office of Information Technology # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with Foobar. If not, see <http://www.gnu.org/licenses/>. ########################### # # Module for interacting with on premise gitHub service. # Module is designed to work with both public and private(Enterprise) github APIv3 over https ONLY # https://developer.github.com/enterprise/2.8/v3/ # ########################### #region New-GitHubHeader function New-GitHubHeader { <# .SYNOPSIS Create Header to be consumed by all other functions .DESCRIPTION Create Header to be consumed by all other functions .PARAMETER psCreds PScredential composed of your username/password to Git Server .PARAMETER authToken Use instead of user/pass, personal auth token .NOTES Author: Travis Sobeck LASTEDIT: 6/20/2017 .EXAMPLE #> [CmdletBinding()] param( [Parameter(Mandatory,ParameterSetName='creds')] [System.Management.Automation.PSCredential]$psCreds, [Parameter(Mandatory,ParameterSetName='token')] [string]$authToken ) if ($authToken){return (@{"Authorization" = "token $authToken"})} else{ $auth = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($psCreds.UserName+':'+$psCreds.GetNetworkCredential().Password)) return (@{"Authorization" = "Basic $auth"}) } } #endregion #region Get-GitHubBase function Get-GitHubBase { <# .SYNOPSIS Base for constructing Get commands .DESCRIPTION Base for constructing Get commands .PARAMETER headers Get this from New-GitHubHeader .PARAMETER sha sha for the commit, use Get-GitHubRepoRef to get it .PARAMETER Repo Repository name string which is used to identify which repository under the organization to go into. .PARAMETER Org Organization name string which is used to identify which organization in the GitHub instance to go into. .NOTES Author: Travis Sobeck LASTEDIT: 6/20/2017 .EXAMPLE Get-GitHubCommit -Username "Test" -Password "pass" -Repo "MyFakeReop" -Org "MyFakeOrg" -server "onPremiseServer" -sha $sha #> [CmdletBinding()] param( [System.Collections.Hashtable]$headers, [Parameter(Mandatory)] [string]$Repo, [Parameter(Mandatory)] [string]$Org, [Parameter(Mandatory)] [string]$data, ## The Default is public github but you can se this if you are running your own Enterprise Github server [string]$server = 'github.com' ) if ($server -eq 'github.com'){$conn = "https://api.github.com"} else{$conn = "https://$server/api/v3"} $URI = "$conn/repos/$org/$Repo/git/$data" try{return(Invoke-RestMethod -Method Get -Uri $URI -Headers $Headers)} catch{throw $Error[0]} } #endregion #region Get-GitHubCommit function Get-GitHubCommit { <# .SYNOPSIS Get a specific commit for a specific repo .DESCRIPTION Get a specific commit for a specific repo .PARAMETER headers Get this from New-GitHubHeader .PARAMETER sha sha for the commit, use Get-GitHubRepoRef to get it .PARAMETER Repo Repository name string which is used to identify which repository under the organization to go into. .PARAMETER Org Organization name string which is used to identify which organization in the GitHub instance to go into. .NOTES Name: Get-GitHubCommit Author: Travis Sobeck LASTEDIT: 6/20/2017 .EXAMPLE Get-GitHubCommit -Username "Test" -Password "pass" -Repo "MyFakeReop" -Org "MyFakeOrg" -server "onPremiseServer" -sha $sha #> [CmdletBinding()] param( [System.Collections.Hashtable]$headers, [Parameter(Mandatory)] [string]$Repo, [Parameter(Mandatory)] [string]$Org, [Parameter(Mandatory)] [string]$sha, ## The Default is public github but you can se this if you are running your own Enterprise Github server [string]$server = 'github.com' ) Get-GitHubBase -headers $headers -Repo $Repo -Org $Org -server $server -data "commits/$sha" } #endregion #region Get-GitHubRepoRef function Get-GitHubRepoRef { <# .SYNOPSIS Get a specific reference or all references for a specific repo .DESCRIPTION Get a specific reference or all references for a specific repo, use -ref for a specific reference .PARAMETER headers Get this from New-GitHubHeader .PARAMETER ref Specific ref, run the command without it to get a list example would be. .PARAMETER Repo Repository name string which is used to identify which repository under the organization to go into. .PARAMETER Org Organization name string which is used to identify which organization in the GitHub instance to go into. .NOTES Name: Get-GitHubRepoRefs Author: Travis Sobeck LASTEDIT: 4/26/2017 .EXAMPLE Get-GitHubRepoRefs -Username "Test" -Password "pass" -Repo "MyFakeReop" -Org "MyFakeOrg" -server "onPremiseServer" .EXAMPLE Get-GitHubRepoRefs -Username "Test" -Password "pass" -Repo "MyFakeReop" -Org "MyFakeOrg" -server "onPremiseServer" -ref 'refs/heads/master' #> [CmdletBinding()] [Alias("Get-GitHubRepoRefs")] param( [System.Collections.Hashtable]$headers, [Parameter(Mandatory)] [string]$Repo, [Parameter(Mandatory)] [string]$Org, [string]$ref, ## The Default is public github but you can se this if you are running your own Enterprise Github server [string]$server = 'github.com' ) if (-not($ref)){$ref = 'refs'} Get-GitHubBase -headers $headers -Repo $Repo -Org $Org -server $server -data $ref } #endregion #region Get-GitHubRepoFile function Get-GitHubRepoFile { <# .SYNOPSIS Get a file from a GitHub Repo. .DESCRIPTION Takes in a username, password, filename, repository, organization and a file to output to then downloads the file from the repository. .PARAMETER headers Get this from New-GitHubHeader .PARAMETER File Filename string which needs to be downloaded from the repository. .PARAMETER Repo Repository name string which is used to identify which repository under the organization to go into. .PARAMETER Org Organization name string which is used to identify which organization in the GitHub instance to go into. .PARAMETER OutFile A string representing the local file path to download the GitHub file to. .NOTES Name: Get-GitHubRepoFile Author: Jeff Bolduan LASTEDIT: 4/26/2017 .EXAMPLE Get-GitHubRepoFile -Username "Test" -Password "pass" -File "psscript.ps1" -Repo "MyFakeReop" -Org "MyFakeOrg" -OutFile "C:\temp\psscript.ps1" -server "ServerFQDN" #> [CmdletBinding()] param( [System.Collections.Hashtable]$headers, [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] [string]$File, [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] [string]$Repo, [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] [string]$Org, [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] [string]$OutFile, ## The Default is public github but you can se this if you are running your own Enterprise Github server [string]$server = 'github.com' ) if ($server -eq 'github.com'){$conn = "https://api.github.com"} else{$conn = "https://$server/api/v3"} $URI = "$conn/repos/$org/$Repo/contents/$File" $RESTRequest = Invoke-RestMethod -Method Get -Uri $URI -Headers $Headers if($RESTRequest.download_url -eq $null) { throw [System.IO.IOException] } else { $WebRequest = Invoke-WebRequest -Uri $RESTRequest.download_url -Headers $Headers -OutFile $OutFile } } #endregion #region Get-GitHubRepoFileContent function Get-GitHubRepoFileContent { <# .SYNOPSIS Gets and then returns the contents of a GitHub repo file. .DESCRIPTION Takes in a username, password, filename, repository, organization and then returns the contents of a file from the GitHub repository. .PARAMETER headers Get this from New-GitHubHeader .PARAMETER File Filename string which needs to be downloaded from the repository. This should be the path to the file if it's not in the root of the repository for example "fizz/buzz.txt" and keep in mind it should be the forward slashes. .PARAMETER Repo Repository name string which is used to identify which repository under the organization to go into. .PARAMETER Org Organization name string which is used to identify which organization in the GitHub instance to go into. .PARAMETER Server An optional parameter for the fully qualified domain name of a github server, defaults to github.com but can be an internal enterprise server. .NOTES Name: Get-GitHubRepoFile Author: Jeff Bolduan LASTEDIT: 10/26/2017 .EXAMPLE $GitHubHeaders = New-GitHubHeader -psCreds (Get-Credential) Get-GitHubRepoFile -headers $GitHubHeaders -File "psscript.ps1" -Repo "MyFakeReop" -Org "MyFakeOrg" -server "github.foo.bar" #> [CmdletBinding()] [Alias("Get-GitHubRepoContent")] param( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [System.Collections.Hashtable]$headers, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$File, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$Repo, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$Org, ## The Default is public github but you can set this if you are running your own Enterprise Github server\ [Parameter(Mandatory = $false)] [ValidateNotNullOrEmpty()] [string]$server = 'github.com' ) if ($server -eq 'github.com') { $Connection = "https://api.github.com" } else { $Connection = "https://$server/api/v3" } Write-Verbose -Message "[VariableValue:Connection] :: $Connection" $URI = "$Connection/repos/$org/$Repo/contents/$File" Write-Verbose -Message "[VariableValue:URI] :: $URI" $RESTRequest = Invoke-RestMethod -Method Get -Uri $URI -Headers $Headers Write-Verbose -Message "[VariableValue:RESTRequest] :: $RESTRequest" return([System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($RESTRequest.content))) } #endregion #region Get-GitHubTree function Get-GitHubTree { <# .SYNOPSIS Get a specific reference or all references for a specific repo .DESCRIPTION Get a specific reference or all references for a specific repo, use -ref for a specific reference .PARAMETER headers Get this from New-GitHubHeader .PARAMETER Repo Repository name string which is used to identify which repository under the organization to go into. .PARAMETER Org Organization name string which is used to identify which organization in the GitHub instance to go into. .PARAMETER sha Sha of tree, use (Get-GitHubCommit -authToken $ghAuthToken -Repo $Repo -Org $Org -server $server -sha $sha).tree.sha to get the sha you need .NOTES Name: Get-GitHubRepoRefs Author: Travis Sobeck LASTEDIT: 4/26/2017 .EXAMPLE .EXAMPLE #> [CmdletBinding()] param( [System.Collections.Hashtable]$headers, [Parameter(Mandatory)] [string]$Repo, [Parameter(Mandatory)] [string]$Org, [Parameter(Mandatory)] [string]$sha, [switch]$recurse, ## The Default is public github but you can se this if you are running your own Enterprise Github server [string]$server = 'github.com' ) $data = "trees/$sha" if ($recurse){$data += "?recursive=1"} Get-GitHubBase -headers $headers -Repo $Repo -Org $Org -server $server -data $data } #endregion #region Get-GitHubRepoUnZipped function Get-GitHubRepoUnZipped { <# .SYNOPSIS Get a GitHub Repo. .DESCRIPTION Takes in a username, password, repository, organization and a folder to output to then downloads the files from the repository. .PARAMETER headers Get this from New-GitHubHeader .PARAMETER Repo Repository name string which is used to identify which repository under the organization to go into. .PARAMETER Org Organization name string which is used to identify which organization in the GitHub instance to go into. .PARAMETER OutFolder A string representing the local file path to download the GitHub Reop file to. .EXAMPLE Get-GitHubRepoUnZipped -authToken $authToken -Repo $repo -Org $org -OutFolder $pathToFolder. #> [CmdletBinding()] param( [System.Collections.Hashtable]$headers, [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] [string]$Org, [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] [string]$repo, [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] [string]$outFolder, [Parameter(Mandatory=$true)] [string]$ref = "master", ## The Default is public github but you can se this if you are running your own Enterprise Github server [string]$server = 'github.com' ) # validate folder exits # if (-not(Test-Path $outFolder)){$null = New-Item $outFolder -ItemType Directory -Force} # get zip Get-GitHubRepoZip -Org $org -repo $repo -OutFile "$outFolder\$repo.zip" -ref $ref -server $server -headers $headers # unzip Expand-Archive -Path "$outFolder\$repo.zip" -DestinationPath $outFolder -Force # get the actual folder name, yeah this looks funky but OpenRead locks the file, this is need to get the folder name and still remove the zip file later $scriptblock = {Param( [string]$path) $null = [Reflection.Assembly]::LoadWithPartialName('System.IO.Compression.FileSystem') $folder = [IO.Compression.ZipFile]::OpenRead((Get-Item -Path $path).FullName).Entries[0].FullName $folder = $folder.TrimEnd('/') # trip the stupid trailing slash $folder } $job = Start-Job -Name 'temp' -ArgumentList "$outFolder\$repo.zip" -ScriptBlock $scriptblock do{Start-Sleep -Seconds 1}until((Get-Job -Id $job.Id).State -eq 'Completed') $folder = Receive-Job -Job $job # rename the folder the the repo Rename-Item -Path "$outFolder\$folder" -NewName "$repo"-Force Remove-Item -Path "$outFolder\$repo.zip" -Force } #endregion #region Get-GitHubRepoZip function Get-GitHubRepoZip { <# .SYNOPSIS Get a GitHub Repo and download to zip file. .DESCRIPTION Takes in a PSCredention or Auth Key if needed, repository, organization and a file to output to then downloads the file from the repository. .PARAMETER headers Get this from New-GitHubHeader .PARAMETER Repo Repository name string which is used to identify which repository under the organization to go into. .PARAMETER Org Organization name string which is used to identify which organization in the GitHub instance to go into. .PARAMETER OutFile A string representing the local file path to download the GitHub Zip file to. .EXAMPLE Get-GitHubRepoZip -authToken $authToken -Repo $repo -Org $org -OutFile $outFile -server "ServerFQDN" #> [CmdletBinding()] param( [System.Collections.Hashtable]$headers, [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] [string]$Org, [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] [string]$repo, [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] [string]$OutFile, [Parameter(Mandatory=$true)] [string]$ref = "master", ## The Default is public github but you can se this if you are running your own Enterprise Github server [string]$server = 'github.com' ) if ($server -eq 'github.com'){$conn = "https://api.github.com"} else{$conn = "https://$server/api/v3"} $URI = "$conn/repos/$Org/$repo/zipball/$ref" Invoke-RestMethod -Method Get -Uri $URI -Headers $Headers -OutFile $OutFile } #endregion #region New-GitHubBlob function New-GitHubBlob { <# .SYNOPSIS Create a new Blob .DESCRIPTION Create a new Blob .PARAMETER headers Get this from New-GitHubHeader .PARAMETER Repo Repository name string which is used to identify which repository under the organization to go into. .PARAMETER Org Organization name string which is used to identify which organization in the GitHub instance to go into. .PARAMETER filePath Path to file to be added/modified .NOTES Author: Travis Sobeck LASTEDIT: 6/20/2017 .EXAMPLE #> [CmdletBinding()] param( [Parameter(Mandatory)] [System.Collections.Hashtable]$headers, [Parameter(Mandatory)] [string]$Repo, [Parameter(Mandatory)] [string]$Org, [Parameter(Mandatory)] [string]$filePath, ## The Default is public github but you can se this if you are running your own Enterprise Github server [string]$server = 'github.com' ) if ($server -eq 'github.com'){$conn = "https://api.github.com"} else{$conn = "https://$server/api/v3"} $URI = "$conn/repos/$org/$Repo/git/blobs" $content = $base64 = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes((Get-Content -Path $filePath -Raw))) $json = @{"content" = $content;"encoding" = "base64"} | ConvertTo-Json -Depth 3 Invoke-RestMethod -Method Post -Uri $URI -Headers $Headers -Body $json } #endregion #region New-GitHubCommit function New-GitHubCommit { <# .SYNOPSIS Create a new commit for a specific repo .DESCRIPTION Create a new commit for a specific repo .PARAMETER headers Get this from New-GitHubHeader .PARAMETER Repo Repository name string which is used to identify which repository under the organization to go into. .PARAMETER Org Organization name string which is used to identify which organization in the GitHub instance to go into. .PARAMETER message The commit message .PARAMETER tree The SHA of the tree object this commit points to .PARAMETER parents The SHAs of the commits that were the parents of this commit. If omitted or empty, the commit will be written as a root commit. For a single parent, an array of one SHA should be provided; for a merge commit, an array of more than one should be provided. .NOTES Author: Travis Sobeck LASTEDIT: 6/20/2017 .EXAMPLE #> [CmdletBinding()] param( [Parameter(Mandatory)] [System.Collections.Hashtable]$headers, [Parameter(Mandatory)] [string]$Repo, [Parameter(Mandatory)] [string]$Org, [Parameter(Mandatory)] [string]$message, [Parameter(Mandatory)] [string]$tree, [Parameter(Mandatory)] [array]$parents, ## The Default is public github but you can se this if you are running your own Enterprise Github server [string]$server = 'github.com' ) if ($server -eq 'github.com'){$conn = "https://api.github.com"} else{$conn = "https://$server/api/v3"} $URI = "$conn/repos/$org/$Repo/git/commits" $content = Get-Content $filePath $json = @{"message" = $message; "parents" = $parents; "tree" = $tree} | ConvertTo-Json -Depth 3 Invoke-RestMethod -Method Post -Uri $URI -Headers $Headers -Body $json } #endregion #region New-GitHubTree function New-GitHubTree { <# .SYNOPSIS Get a specific commit for a specific repo .DESCRIPTION Get a specific commit for a specific repo, .PARAMETER headers Get this from New-GitHubHeader .PARAMETER Repo Repository name string which is used to identify which repository under the organization to go into. .PARAMETER Org Organization name string which is used to identify which organization in the GitHub instance to go into. .PARAMETER path path to file in github .PARAMETER baseTree This is the SHA for the tree this is getting added onto, use Get-GitHubCommit and store ie $treeSha = $commit.tree.sha .PARAMETER mode The file mode; one of 100644 for file (blob), 100755 for executable (blob), 040000 for subdirectory (tree), 160000 for submodule (commit), or 120000 for a blob that specifies the path of a symlink .PARAMETER type Either blob, tree, or commit .PARAMETER blobSha Sha from the blob containing the content, use New-GitHubBlob and record the return sha .NOTES Author: Travis Sobeck LASTEDIT: 6/20/2017 .EXAMPLE #> [CmdletBinding()] param( [Parameter(Mandatory)] [System.Collections.Hashtable]$headers, [Parameter(Mandatory)] [string]$Repo, [Parameter(Mandatory)] [string]$Org, [Parameter(Mandatory)] [string]$baseTree, [Parameter(Mandatory)] [string]$path, [Parameter(Mandatory)] [string]$mode, [Parameter(Mandatory)] [string]$type, [Parameter(Mandatory)] [string]$blobSha, ## The Default is public github but you can se this if you are running your own Enterprise Github server [string]$server = 'github.com' ) if ($server -eq 'github.com'){$conn = "https://api.github.com"} else{$conn = "https://$server/api/v3"} $URI = "$conn/repos/$org/$Repo/git/trees" $json = @{"base_tree" = $baseTree;"tree" = @(@{"path" = $path;"mode"=$mode;"type" = $type;"sha" = $blobSha})} | ConvertTo-Json -Depth 3 Invoke-RestMethod -Method Post -Uri $URI -Headers $Headers -Body $json } #endregion #region Set-GitHubCommit function Set-GitHubCommit { <# .SYNOPSIS Update a reference to a new Commit .DESCRIPTION Update a reference to a new Commit .PARAMETER headers Get this from New-GitHubHeader .PARAMETER Repo Repository name string which is used to identify which repository under the organization to go into. .PARAMETER Org Organization name string which is used to identify which organization in the GitHub instance to go into. .PARAMETER ref ref to be updated .PARAMETER sha SHA to update reference to, get this from New-GitHubCommit .PARAMETER parents The SHAs of the commits that were the parents of this commit. If omitted or empty, the commit will be written as a root commit. For a single parent, an array of one SHA should be provided; for a merge commit, an array of more than one should be provided. .NOTES Author: Travis Sobeck LASTEDIT: 6/20/2017 .EXAMPLE #> [CmdletBinding()] param( [Parameter(Mandatory)] [System.Collections.Hashtable]$headers, [Parameter(Mandatory)] [string]$Repo, [Parameter(Mandatory)] [string]$Org, [Parameter(Mandatory)] [string]$ref, [Parameter(Mandatory)] [string]$sha, ## The Default is public github but you can se this if you are running your own Enterprise Github server [string]$server = 'github.com' ) if ($server -eq 'github.com'){$conn = "https://api.github.com"} else{$conn = "https://$server/api/v3"} $URI = "$conn/repos/$org/$Repo/git/$ref" $json = @{"sha" = $sha;"force"=$true} | ConvertTo-Json -Depth 3 Invoke-RestMethod -Method Patch -Uri $URI -Headers $Headers -Body $json } #endregion #region Update-GitHubRepo function Update-GitHubRepo { <# .SYNOPSIS Get a specific reference or all references for a specific repo .DESCRIPTION Get a specific reference or all references for a specific repo, use -ref for a specific reference .PARAMETER headers Get this from New-GitHubHeader .PARAMETER ref Specific ref, run the command without it to get a list example would be. .PARAMETER Repo Repository name string which is used to identify which repository under the organization to go into. .PARAMETER Org Organization name string which is used to identify which organization in the GitHub instance to go into. .PARAMETER message The commit message .PARAMETER path path to file in github .PARAMETER filePath Path to file to be added/modified .NOTES Author: Travis Sobeck LASTEDIT: 4/26/2017 .EXAMPLE #> [CmdletBinding()] param( [Parameter(Mandatory)] [System.Collections.Hashtable]$headers, [Parameter(Mandatory)] [string]$Repo, [Parameter(Mandatory)] [string]$Org, [Parameter(Mandatory)] [string]$ref, [Parameter(Mandatory)] [string]$message, [Parameter(Mandatory)] [string]$path, [Parameter(Mandatory)] [string]$filePath, ## The Default is public github but you can se this if you are running your own Enterprise Github server [string]$server = 'github.com' ) try{ # Get reference to head of ref provided and record Sha $reference = Get-GitHubRepoRef -headers $headers -Repo $Repo -Org $Org -server $server -ref $ref $sha = $reference.object.sha # get commit for that ref and store Sha $commit = Get-GitHubCommit -headers $headers -Repo $Repo -Org $Org -server $server -sha $sha $treeSha = $commit.tree.sha # Creat Blob $blob = New-GitHubBlob -headers $headers -Repo $Repo -Org $Org -server $server -filePath $filePath # create new Tree $tree = New-GitHubTree -headers $headers -Repo $Repo -Org $Org -server $server -path $path -blobSha $blob.sha -baseTree $treeSha -mode 100644 -type 'blob' # create new commit $newCommit = New-GitHubCommit -headers $headers -Repo $Repo -Org $Org -server $server -message $message -tree $tree.sha -parents @($sha) # update head to point at new commint Set-GitHubCommit -headers $headers -Repo $Repo -Org $Org -server $server -ref $ref -sha $newCommit.sha } catch{$Error[0]} } #endregion ########################################################################################################################## Export-ModuleMember -Function * # SIG # Begin signature block # MIIaxgYJKoZIhvcNAQcCoIIatzCCGrMCAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB # gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR # AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUC7moU9utR2SWqBzhVQ5xi4nW # TLGgghW3MIIEmTCCA4GgAwIBAgIPFojwOSVeY45pFDkH5jMLMA0GCSqGSIb3DQEB # BQUAMIGVMQswCQYDVQQGEwJVUzELMAkGA1UECBMCVVQxFzAVBgNVBAcTDlNhbHQg # TGFrZSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxITAfBgNV # BAsTGGh0dHA6Ly93d3cudXNlcnRydXN0LmNvbTEdMBsGA1UEAxMUVVROLVVTRVJG # aXJzdC1PYmplY3QwHhcNMTUxMjMxMDAwMDAwWhcNMTkwNzA5MTg0MDM2WjCBhDEL # MAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE # BxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKjAoBgNVBAMT # IUNPTU9ETyBTSEEtMSBUaW1lIFN0YW1waW5nIFNpZ25lcjCCASIwDQYJKoZIhvcN # AQEBBQADggEPADCCAQoCggEBAOnpPd/XNwjJHjiyUlNCbSLxscQGBGue/YJ0UEN9 # xqC7H075AnEmse9D2IOMSPznD5d6muuc3qajDjscRBh1jnilF2n+SRik4rtcTv6O # KlR6UPDV9syR55l51955lNeWM/4Og74iv2MWLKPdKBuvPavql9LxvwQQ5z1IRf0f # aGXBf1mZacAiMQxibqdcZQEhsGPEIhgn7ub80gA9Ry6ouIZWXQTcExclbhzfRA8V # zbfbpVd2Qm8AaIKZ0uPB3vCLlFdM7AiQIiHOIiuYDELmQpOUmJPv/QbZP7xbm1Q8 # ILHuatZHesWrgOkwmt7xpD9VTQoJNIp1KdJprZcPUL/4ygkCAwEAAaOB9DCB8TAf # BgNVHSMEGDAWgBTa7WR0FJwUPKvdmam9WyhNizzJ2DAdBgNVHQ4EFgQUjmstM2v0 # M6eTsxOapeAK9xI1aogwDgYDVR0PAQH/BAQDAgbAMAwGA1UdEwEB/wQCMAAwFgYD # VR0lAQH/BAwwCgYIKwYBBQUHAwgwQgYDVR0fBDswOTA3oDWgM4YxaHR0cDovL2Ny # bC51c2VydHJ1c3QuY29tL1VUTi1VU0VSRmlyc3QtT2JqZWN0LmNybDA1BggrBgEF # BQcBAQQpMCcwJQYIKwYBBQUHMAGGGWh0dHA6Ly9vY3NwLnVzZXJ0cnVzdC5jb20w # DQYJKoZIhvcNAQEFBQADggEBALozJEBAjHzbWJ+zYJiy9cAx/usfblD2CuDk5oGt # Joei3/2z2vRz8wD7KRuJGxU+22tSkyvErDmB1zxnV5o5NuAoCJrjOU+biQl/e8Vh # f1mJMiUKaq4aPvCiJ6i2w7iH9xYESEE9XNjsn00gMQTZZaHtzWkHUxY93TYCCojr # QOUGMAu4Fkvc77xVCf/GPhIudrPczkLv+XZX4bcKBUCYWJpdcRaTcYxlgepv84n3 # +3OttOe/2Y5vqgtPJfO44dXddZhogfiqwNGAwsTEOYnB9smebNd0+dmX+E/CmgrN # Xo/4GengpZ/E8JIh5i15Jcki+cPwOoRXrToW9GOUEB1d0MYwggV3MIIEX6ADAgEC # AhAT6ihwW/Ts7Qw2YwmAYUM2MA0GCSqGSIb3DQEBDAUAMG8xCzAJBgNVBAYTAlNF # MRQwEgYDVQQKEwtBZGRUcnVzdCBBQjEmMCQGA1UECxMdQWRkVHJ1c3QgRXh0ZXJu # YWwgVFRQIE5ldHdvcmsxIjAgBgNVBAMTGUFkZFRydXN0IEV4dGVybmFsIENBIFJv # b3QwHhcNMDAwNTMwMTA0ODM4WhcNMjAwNTMwMTA0ODM4WjCBiDELMAkGA1UEBhMC # VVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4w # HAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVz # dCBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUA # A4ICDwAwggIKAoICAQCAEmUXNg7D2wiz0KxXDXbtzSfTTK1Qg2HiqiBNCS1kCdzO # iZ/MPans9s/B3PHTsdZ7NygRK0faOca8Ohm0X6a9fZ2jY0K2dvKpOyuR+OJv0OwW # IJAJPuLodMkYtJHUYmTbf6MG8YgYapAiPLz+E/CHFHv25B+O1ORRxhFnRghRy4YU # VD+8M/5+bJz/Fp0YvVGONaanZshyZ9shZrHUm3gDwFA66Mzw3LyeTP6vBZY1H1da # t//O+T23LLb2VN3I5xI6Ta5MirdcmrS3ID3KfyI0rn47aGYBROcBTkZTmzNg95S+ # UzeQc0PzMsNT79uq/nROacdrjGCT3sTHDN/hMq7MkztReJVni+49Vv4M0GkPGw/z # JSZrM233bkf6c0Plfg6lZrEpfDKEY1WJxA3Bk1QwGROs0303p+tdOmw1XNtB1xLa # qUkL39iAigmTYo61Zs8liM2EuLE/pDkP2QKe6xJMlXzzawWpXhaDzLhn4ugTncxb # gtNMs+1b/97lc6wjOy0AvzVVdAlJ2ElYGn+SNuZRkg7zJn0cTRe8yexDJtC/QV9A # qURE9JnnV4eeUB9XVKg+/XRjL7FQZQnmWEIuQxpMtPAlR1n6BB6T1CZGSlCBst6+ # eLf8ZxXhyVeEHg9j1uliutZfVS7qXMYoCAQlObgOK6nyTJccBz8NUvXt7y+CDwID # AQABo4H0MIHxMB8GA1UdIwQYMBaAFK29mHo0tCb3+sQmVO8DveAky1QaMB0GA1Ud # DgQWBBRTeb9aqitKz1SA4dibwJ3ysgNmyzAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0T # AQH/BAUwAwEB/zARBgNVHSAECjAIMAYGBFUdIAAwRAYDVR0fBD0wOzA5oDegNYYz # aHR0cDovL2NybC51c2VydHJ1c3QuY29tL0FkZFRydXN0RXh0ZXJuYWxDQVJvb3Qu # Y3JsMDUGCCsGAQUFBwEBBCkwJzAlBggrBgEFBQcwAYYZaHR0cDovL29jc3AudXNl # cnRydXN0LmNvbTANBgkqhkiG9w0BAQwFAAOCAQEAk2X2N4OVD17Dghwf1nfnPIrA # qgnw6Qsm8eDCanWhx3nJuVJgyCkSDvCtA9YJxHbf5aaBladG2oJXqZWSxbaPAyJs # M3fBezIXbgfOWhRBOgUkG/YUBjuoJSQOu8wqdd25cEE/fNBjNiEHH0b/YKSR4We8 # 3h9+GRTJY2eR6mcHa7SPi8BuQ33DoYBssh68U4V93JChpLwt70ZyVzUFv7tGu25t # N5m2/yOSkcZuQPiPKVbqX9VfFFOs8E9h6vcizKdWC+K4NB8m2XsZBWg/ujzUOAai # 0+aPDuO0cW1AQsWEtECVK/RloEh59h2BY5adT3Xg+HzkjqnR8q2Ks4zHIc3C7zCC # BawwggSUoAMCAQICEHJNXiAT1cKRQFXzfFSJVHEwDQYJKoZIhvcNAQELBQAwfDEL # MAkGA1UEBhMCVVMxCzAJBgNVBAgTAk1JMRIwEAYDVQQHEwlBbm4gQXJib3IxEjAQ # BgNVBAoTCUludGVybmV0MjERMA8GA1UECxMISW5Db21tb24xJTAjBgNVBAMTHElu # Q29tbW9uIFJTQSBDb2RlIFNpZ25pbmcgQ0EwHhcNMTcxMjE0MDAwMDAwWhcNMjAx # MjEzMjM1OTU5WjCByzELMAkGA1UEBhMCVVMxDjAMBgNVBBEMBTU1NDU1MRIwEAYD # VQQIDAlNaW5uZXNvdGExFDASBgNVBAcMC01pbm5lYXBvbGlzMRgwFgYDVQQJDA8x # MDAgVW5pb24gU3QgU0UxIDAeBgNVBAoMF1VuaXZlcnNpdHkgb2YgTWlubmVzb3Rh # MSQwIgYDVQQLDBtDb21wdXRlciBhbmQgRGV2aWNlIFN1cHBvcnQxIDAeBgNVBAMM # F1VuaXZlcnNpdHkgb2YgTWlubmVzb3RhMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A # MIIBCgKCAQEAwk6kLE9u+tWv0JUkIJSn5pWfa09g6cqFLucCXomNj9NYj8t+JfPn # a3gC6LHv3OQAUDHOoC5H+8N3ea7qVGYIiwPRHzXOGqG/tVaiU5s5hG3vBhfRX8W1 # /2g4/hpgeXUzrxYn/2c5SOGGy0MU1ZJyUSFEdsjXHEV7HXK4qmFGV9RJxtiLZH1q # UldCglxcj7zw0QnUdG6oAxpwTCeVp057/WXbnIR8a0gPse+y/new5+CBUGTAvrw6 # K2BrJQVsdIIVn/q+BbcZxh9PpeZfTtsi6lgkvy0bUWtl5sSpd75+hvw4Sl3HAaWZ # toWN7LPmbDbbVRO2Arv4doh4Chod4wJ5xQIDAQABo4IB2DCCAdQwHwYDVR0jBBgw # FoAUrjUjF///Bj2cUOCMJGUzHnAQiKIwHQYDVR0OBBYEFF4LEhElVUvT8n5txOJS # NAczooSAMA4GA1UdDwEB/wQEAwIHgDAMBgNVHRMBAf8EAjAAMBMGA1UdJQQMMAoG # CCsGAQUFBwMDMBEGCWCGSAGG+EIBAQQEAwIEEDBmBgNVHSAEXzBdMFsGDCsGAQQB # riMBBAMCATBLMEkGCCsGAQUFBwIBFj1odHRwczovL3d3dy5pbmNvbW1vbi5vcmcv # Y2VydC9yZXBvc2l0b3J5L2Nwc19jb2RlX3NpZ25pbmcucGRmMEkGA1UdHwRCMEAw # PqA8oDqGOGh0dHA6Ly9jcmwuaW5jb21tb24tcnNhLm9yZy9JbkNvbW1vblJTQUNv # ZGVTaWduaW5nQ0EuY3JsMH4GCCsGAQUFBwEBBHIwcDBEBggrBgEFBQcwAoY4aHR0 # cDovL2NydC5pbmNvbW1vbi1yc2Eub3JnL0luQ29tbW9uUlNBQ29kZVNpZ25pbmdD # QS5jcnQwKAYIKwYBBQUHMAGGHGh0dHA6Ly9vY3NwLmluY29tbW9uLXJzYS5vcmcw # GQYDVR0RBBIwEIEOb2l0bXB0QHVtbi5lZHUwDQYJKoZIhvcNAQELBQADggEBAENR # lesMKmBaZ0g68lttYEMtaPiz+DaNpOlXBs1gH66aghB1aP6iiRJcFVasGLUVFncd # G1xbw503LTrBUc5PECMVDVF7KKCfHA1OeFV9vOWyvdVgbe3paDy1sj4CADO2D0gn # xcGiZoFhEZiBkTvSsj4S3GXZEvoFHJxJLw2kvdLnzy0gH/b/b/yblwA1fKXw4loc # UpDM6qTwM7SiKgkQ5W7/280EYu8BI6c8rpiJmqM1tZLcpswuavB00T52Y+ZZmz3t # MMVgFHn9pFFltYr3s3bEek7I6pU8unISbiyQzxqhIUKaBi8hy8LgoY5UnGjX5jHs # IvINzms+JX5Ity02sL0wggXrMIID06ADAgECAhBl4eLj1d5QRYXzJiSABeLUMA0G # CSqGSIb3DQEBDQUAMIGIMQswCQYDVQQGEwJVUzETMBEGA1UECBMKTmV3IEplcnNl # eTEUMBIGA1UEBxMLSmVyc2V5IENpdHkxHjAcBgNVBAoTFVRoZSBVU0VSVFJVU1Qg # TmV0d29yazEuMCwGA1UEAxMlVVNFUlRydXN0IFJTQSBDZXJ0aWZpY2F0aW9uIEF1 # dGhvcml0eTAeFw0xNDA5MTkwMDAwMDBaFw0yNDA5MTgyMzU5NTlaMHwxCzAJBgNV # BAYTAlVTMQswCQYDVQQIEwJNSTESMBAGA1UEBxMJQW5uIEFyYm9yMRIwEAYDVQQK # EwlJbnRlcm5ldDIxETAPBgNVBAsTCEluQ29tbW9uMSUwIwYDVQQDExxJbkNvbW1v # biBSU0EgQ29kZSBTaWduaW5nIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB # CgKCAQEAwKAvix56u2p1rPg+3KO6OSLK86N25L99MCfmutOYMlYjXAaGlw2A6O2i # gTXrC/Zefqk+aHP9ndRnec6q6mi3GdscdjpZh11emcehsriphHMMzKuHRhxqx+85 # Jb6n3dosNXA2HSIuIDvd4xwOPzSf5X3+VYBbBnyCV4RV8zj78gw2qblessWBRyN9 # EoGgwAEoPgP5OJejrQLyAmj91QGr9dVRTVDTFyJG5XMY4DrkN3dRyJ59UopPgNwm # ucBMyvxR+hAJEXpXKnPE4CEqbMJUvRw+g/hbqSzx+tt4z9mJmm2j/w2nP35MViPW # Cb7hpR2LB8W/499Yqu+kr4LLBfgKCQIDAQABo4IBWjCCAVYwHwYDVR0jBBgwFoAU # U3m/WqorSs9UgOHYm8Cd8rIDZsswHQYDVR0OBBYEFK41Ixf//wY9nFDgjCRlMx5w # EIiiMA4GA1UdDwEB/wQEAwIBhjASBgNVHRMBAf8ECDAGAQH/AgEAMBMGA1UdJQQM # MAoGCCsGAQUFBwMDMBEGA1UdIAQKMAgwBgYEVR0gADBQBgNVHR8ESTBHMEWgQ6BB # hj9odHRwOi8vY3JsLnVzZXJ0cnVzdC5jb20vVVNFUlRydXN0UlNBQ2VydGlmaWNh # dGlvbkF1dGhvcml0eS5jcmwwdgYIKwYBBQUHAQEEajBoMD8GCCsGAQUFBzAChjNo # dHRwOi8vY3J0LnVzZXJ0cnVzdC5jb20vVVNFUlRydXN0UlNBQWRkVHJ1c3RDQS5j # cnQwJQYIKwYBBQUHMAGGGWh0dHA6Ly9vY3NwLnVzZXJ0cnVzdC5jb20wDQYJKoZI # hvcNAQENBQADggIBAEYstn9qTiVmvZxqpqrQnr0Prk41/PA4J8HHnQTJgjTbhuET # 98GWjTBEE9I17Xn3V1yTphJXbat5l8EmZN/JXMvDNqJtkyOh26owAmvquMCF1pKi # QWyuDDllxR9MECp6xF4wnH1Mcs4WeLOrQPy+C5kWE5gg/7K6c9G1VNwLkl/po9OR # PljxKKeFhPg9+Ti3JzHIxW7LdyljffccWiuNFR51/BJHAZIqUDw3LsrdYWzgg4x0 # 6tgMvOEf0nITelpFTxqVvMtJhnOfZbpdXZQ5o1TspxfTEVOQAsp05HUNCXyhznlV # Lr0JaNkM7edgk59zmdTbSGdMq8Ztuu6VyrivOlMSPWmay5MjvwTzuNorbwBv0DL+ # 7cyZBp7NYZou+DoGd1lFZN0jU5IsQKgm3+00pnnJ67crdFwfz/8bq3MhTiKOWEb0 # 4FT3OZVp+jzvaChHWLQ8gbCORgClaZq1H3aqI7JeRkWEEEp6Tv4WAVsr/i7LoXU7 # 2gOb8CAzPFqwI4Excdrxp0I4OXbECHlDqU4sTInqwlMwofmxeO4u94196qIqJQl+ # 8Sykl06VktqMux84Iw3ZQLH08J8LaJ+WDUycc4OjY61I7FGxCDkbSQf3npXeRFm0 # IBn8GiW+TRDk6J2XJFLWEtVZmhboFlBLoUlqHUCKu0QOhU/+AEOqnY98j2zRMYIE # eTCCBHUCAQEwgZAwfDELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAk1JMRIwEAYDVQQH # EwlBbm4gQXJib3IxEjAQBgNVBAoTCUludGVybmV0MjERMA8GA1UECxMISW5Db21t # b24xJTAjBgNVBAMTHEluQ29tbW9uIFJTQSBDb2RlIFNpZ25pbmcgQ0ECEHJNXiAT # 1cKRQFXzfFSJVHEwCQYFKw4DAhoFAKB4MBgGCisGAQQBgjcCAQwxCjAIoAKAAKEC # gAAwGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEOMAwG # CisGAQQBgjcCARUwIwYJKoZIhvcNAQkEMRYEFK4ITJZDJCgj49OPY3bSQ1i1ojlD # MA0GCSqGSIb3DQEBAQUABIIBADmNvYG7vLPuQzZV1IwVc6kSL+34chm3r0MuRIie # u1IaQuufCEw6CFchf48z+Pj7LswFu3/Kaoi2PkbGl5R54fZu0434CtFuADwpJP2m # 1aDVU1Q07LZ+1P5Hf8Wws7KliZdG2KJt+3oLrul7a54o61pFMQqVcJFvmmUr5nN5 # 2RFrVMjWmaw44+LIDeYD4hh34N90TxNsO6VOdLfkMZigLlOrmnk7gO69bnRc7H3a # YNPV4dh0Kp4LYROq8+Z/LqAj8Ep5U/JB3sVncFLS0ImiLDzLGZ/t+Y+BJf4bxdJt # sSjVxOVJdiwM8XUl3yFtkVd0Fz7kBBKaUaeb6+/Q96JcJaShggJDMIICPwYJKoZI # hvcNAQkGMYICMDCCAiwCAQEwgakwgZUxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJV # VDEXMBUGA1UEBxMOU2FsdCBMYWtlIENpdHkxHjAcBgNVBAoTFVRoZSBVU0VSVFJV # U1QgTmV0d29yazEhMB8GA1UECxMYaHR0cDovL3d3dy51c2VydHJ1c3QuY29tMR0w # GwYDVQQDExRVVE4tVVNFUkZpcnN0LU9iamVjdAIPFojwOSVeY45pFDkH5jMLMAkG # BSsOAwIaBQCgXTAYBgkqhkiG9w0BCQMxCwYJKoZIhvcNAQcBMBwGCSqGSIb3DQEJ # BTEPFw0xODAyMTYxOTIwMzZaMCMGCSqGSIb3DQEJBDEWBBSzEiVBR1sIMTO9pJJt # 8mBjCpG18DANBgkqhkiG9w0BAQEFAASCAQA6zFNHdK+yxtYBpYacb5txvtXu/043 # s07+UX1xxw8YYz/obpnsaTPEmb7h8YMD2e0kLOx39XLkE8IPpfk/n9hIPFXhjJhm # dqK3NDiAZ5ciMp9zXePwqhJ4nGHnmIOTOBJO62Nsfe1fXRi7jBZ9lPolKKcBh0cv # Vw+T5+EXnoZxpT8BdQNcToOJBuFJsO6fEEHT/XPQZwwx0hh4s55ENr4sXc6UYubt # Y/K8hjV/8r+C/6BeupLJi2Bw9Jd2gPUHvCvz67kSuoeRhDSSQyc9a70wXmugUFw7 # 5oNQ5FLpUzj+Y2pbvWBGdpTOZohn6MaXXDzqrTMzT+NANxhgipv8zv78 # SIG # End signature block |