2019-12-26 23:01:54 -05:00
// Copyright Epic Games, Inc. All Rights Reserved.
2014-03-14 14:13:41 -04:00
using System ;
using System.Collections.Generic ;
using System.ComponentModel ;
using System.Data ;
using System.Drawing ;
using System.Linq ;
using System.Text ;
using System.Windows.Forms ;
using System.IO ;
using System.Security.Cryptography.X509Certificates ;
using System.Diagnostics ;
2014-11-13 17:30:33 -05:00
using System.Runtime.InteropServices ;
2014-03-14 14:13:41 -04:00
namespace iPhonePackager
{
public partial class ToolsHub : Form
{
2014-11-13 17:30:33 -05:00
[DllImport("user32.dll")]
public static extern int MessageBox ( IntPtr hWnd , String Text , String Caption , uint type ) ;
2014-10-01 15:15:07 -04:00
protected Dictionary < int , Bitmap > CheckStateImages = new Dictionary < int , Bitmap > ( ) ;
2014-03-14 14:13:41 -04:00
public static string IpaFilter = "iOS packaged applications (*.ipa)|*.ipa|All Files (*.*)|*.*" ;
public static string CertificateRequestFilter = "Certificate Request (*.csr)|*.csr|All Files (*.*)|*.*" ;
public static string MobileProvisionFilter = "Mobile provision files (*.mobileprovision)|*.mobileprovision|All files (*.*)|*.*" ;
public static string CertificatesFilter = "Code signing certificates (*.cer;*.p12)|*.cer;*.p12|All files (*.*)|*.*" ;
public static string KeysFilter = "Keys (*.key;*.p12)|*.key;*.p12|All files (*.*)|*.*" ;
public static string JustXmlKeysFilter = "XML Public/Private Key Pair (*.key)|*.key|All Files (*.*)|*.*" ;
public static string PListFilter = "Property Lists (*.plist)|*.plist|All Files (*.*)|*.*" ;
public static string JustP12Certificates = "Code signing certificates (*.p12)|*.p12|All files (*.*)|*.*" ;
//@TODO
public static string ChoosingFilesToInstallDirectory = null ;
public static string ChoosingIpaDirectory = null ;
/// <summary>
/// Shows a file dialog, preserving the current working directory of the application
/// </summary>
/// <param name="Filter">Filter string for the dialog</param>
/// <param name="Title">Display title for the dialog</param>
/// <param name="DefaultExtension">Default extension</param>
/// <param name="StartingFilename">Initial filename (can be null)</param>
/// <param name="SavedDirectory">Starting directory for the dialog (will be updated on a successful pick)</param>
/// <param name="OutFilename">(out) The picked filename, or null if the dialog was cancelled </param>
/// <returns>True on a successful pick (filename is in OutFilename)</returns>
public static bool ShowOpenFileDialog ( string Filter , string Title , string DefaultExtension , string StartingFilename , ref string SavedDirectory , out string OutFilename )
{
// Save off the current working directory
string CWD = Directory . GetCurrentDirectory ( ) ;
// Show the dialog
System . Windows . Forms . OpenFileDialog OpenDialog = new System . Windows . Forms . OpenFileDialog ( ) ;
OpenDialog . DefaultExt = DefaultExtension ;
OpenDialog . FileName = ( StartingFilename ! = null ) ? StartingFilename : "" ;
OpenDialog . Filter = Filter ;
OpenDialog . Title = Title ;
OpenDialog . InitialDirectory = ( SavedDirectory ! = null ) ? SavedDirectory : CWD ;
bool bDialogSucceeded = OpenDialog . ShowDialog ( ) = = DialogResult . OK ;
// Restore the current working directory
Directory . SetCurrentDirectory ( CWD ) ;
if ( bDialogSucceeded )
{
SavedDirectory = Path . GetDirectoryName ( OpenDialog . FileName ) ;
OutFilename = OpenDialog . FileName ;
return true ;
}
else
{
OutFilename = null ;
return false ;
}
}
public ToolsHub ( )
{
InitializeComponent ( ) ;
2014-10-01 15:15:07 -04:00
CheckStateImages . Add ( 0 , iPhonePackager . Properties . Resources . GreyCheck ) ;
CheckStateImages . Add ( 1 , iPhonePackager . Properties . Resources . YellowCheck ) ;
CheckStateImages . Add ( 2 , iPhonePackager . Properties . Resources . GreenCheck ) ;
2014-03-14 14:13:41 -04:00
Text = Config . AppDisplayName + " Wizard" ;
}
public static ToolsHub CreateShowingTools ( )
{
ToolsHub Result = new ToolsHub ( ) ;
2014-08-04 18:29:09 -04:00
Result . tabControl1 . SelectTab ( Result . tabPage1 ) ;
2014-03-14 14:13:41 -04:00
return Result ;
}
private void ToolsHub_Shown ( object sender , EventArgs e )
{
// Check the status of the various steps
UpdateStatus ( ) ;
2014-08-01 19:19:55 -04:00
CreateCSRButton . Enabled =
ImportMobileProvisionButton . Enabled =
ImportProvisionButton2 . Enabled =
ImportCertificateButton . Enabled =
ImportCertificateButton2 . Enabled =
! string . IsNullOrEmpty ( Config . ProjectFile ) ;
2014-03-14 14:13:41 -04:00
}
void UpdateStatus ( )
{
MobileProvision Provision ;
X509Certificate2 Cert ;
bool bOverridesExists ;
2014-10-01 15:15:07 -04:00
bool bNameMatch ;
CodeSignatureBuilder . FindRequiredFiles ( out Provision , out Cert , out bOverridesExists , out bNameMatch , false ) ;
2014-03-14 14:13:41 -04:00
2014-10-01 15:15:07 -04:00
int ProvisionVal = 0 ;
if ( Provision ! = null )
{
ProvisionVal = 1 ;
if ( bNameMatch )
{
ProvisionVal = 2 ;
}
}
int CertVal = 0 ;
if ( Cert ! = null )
{
CertVal = 2 ;
}
MobileProvisionCheck2 . Image = MobileProvisionCheck . Image = CheckStateImages [ ProvisionVal ] ;
CertificatePresentCheck2 . Image = CertificatePresentCheck . Image = CheckStateImages [ CertVal ] ;
2014-08-01 18:29:52 -04:00
// OverridesPresentCheck2.Image = OverridesPresentCheck.Image = CheckStateImages[bOverridesExists];
2014-03-14 14:13:41 -04:00
2014-08-01 18:29:52 -04:00
// ReadyToPackageButton.Enabled = /*bOverridesExists && */(Provision != null) && (Cert != null);
2014-03-14 14:13:41 -04:00
}
private void CreateCSRButton_Click ( object sender , EventArgs e )
{
GenerateSigningRequestDialog Dlg = new GenerateSigningRequestDialog ( ) ;
Dlg . ShowDialog ( this ) ;
}
public static void ShowError ( string Message )
{
2014-11-13 17:30:33 -05:00
System . Windows . Forms . MessageBox . Show ( Message , Config . AppDisplayName , MessageBoxButtons . OK , MessageBoxIcon . Error ) ;
2014-03-14 14:13:41 -04:00
}
public static bool IsProfileForDistribution ( MobileProvision Provision )
{
return CryptoAdapter . GetCommonNameFromCert ( Provision . DeveloperCertificates [ 0 ] ) . IndexOf ( "iPhone Distribution" , StringComparison . InvariantCultureIgnoreCase ) > = 0 ;
}
2014-11-07 15:46:06 -05:00
public static void TryInstallingMobileProvision ( string ProvisionFilename , bool ShowPrompt = true )
2014-03-14 14:13:41 -04:00
{
2014-11-07 15:46:06 -05:00
if ( ! String . IsNullOrEmpty ( ProvisionFilename ) | | ShowOpenFileDialog ( MobileProvisionFilter , "Choose a mobile provision to install" , "mobileprovision" , "" , ref ChoosingFilesToInstallDirectory , out ProvisionFilename ) )
2014-03-14 14:13:41 -04:00
{
try
{
// Determine if this is a development or distribution certificate
bool bIsDistribution = false ;
MobileProvision Provision = MobileProvisionParser . ParseFile ( ProvisionFilename ) ;
bIsDistribution = IsProfileForDistribution ( Provision ) ;
2014-08-01 18:29:52 -04:00
// use the input filename if the GameName is empty
string DestName = string . IsNullOrEmpty ( Program . GameName ) ? Path . GetFileNameWithoutExtension ( ProvisionFilename ) : Program . GameName ;
2014-03-14 14:13:41 -04:00
// Copy the file into the destination location
string EffectivePrefix = bIsDistribution ? "Distro_" : Config . SigningPrefix ;
2014-08-01 18:29:52 -04:00
string DestinationFilename = Path . Combine ( Config . ProvisionDirectory , EffectivePrefix + DestName + ".mobileprovision" ) ;
2014-03-14 14:13:41 -04:00
2015-01-12 18:29:38 -05:00
DestinationFilename = DestinationFilename . Replace ( "\\" , "/" ) ;
2014-03-14 14:13:41 -04:00
if ( File . Exists ( DestinationFilename ) )
{
MobileProvision OldProvision = MobileProvisionParser . ParseFile ( DestinationFilename ) ;
string MessagePrompt = String . Format (
"{0} already contains a {1} mobile provision file. Do you want to replace the provision '{2}' with '{3}'?" ,
Config . BuildDirectory ,
bIsDistribution ? "distribution" : "development" ,
OldProvision . ProvisionName ,
Provision . ProvisionName ) ;
2014-11-13 17:30:33 -05:00
if ( ShowPrompt & & System . Windows . Forms . MessageBox . Show ( MessagePrompt , Config . AppDisplayName , MessageBoxButtons . YesNo , MessageBoxIcon . Question ) = = DialogResult . No )
2014-03-14 14:13:41 -04:00
{
return ;
}
2015-01-12 18:29:38 -05:00
if ( DestinationFilename ! = ProvisionFilename )
{
FileOperations . DeleteFile ( DestinationFilename ) ;
}
2014-03-14 14:13:41 -04:00
}
2015-01-12 18:29:38 -05:00
if ( DestinationFilename ! = ProvisionFilename )
{
FileOperations . CopyRequiredFile ( ProvisionFilename , DestinationFilename ) ;
}
2014-03-14 14:13:41 -04:00
}
catch ( Exception ex )
{
ShowError ( String . Format ( "Encountered an error '{0} while trying to install a mobile provision" , ex . Message ) ) ;
}
}
}
private void ImportMobileProvisionButton_Click ( object sender , EventArgs e )
{
2014-11-07 15:46:06 -05:00
TryInstallingMobileProvision ( null ) ;
2014-03-14 14:13:41 -04:00
UpdateStatus ( ) ;
}
2014-11-07 15:46:06 -05:00
static private string CertToolData = "" ;
static public void OutputReceivedCertToolProcessCall ( Object Sender , DataReceivedEventArgs Line )
{
if ( ( Line ! = null ) & & ! String . IsNullOrEmpty ( Line . Data ) )
{
CertToolData + = Line . Data + "\n" ;
}
}
public static void TryInstallingCertificate_PromptForKey ( string CertificateFilename , bool ShowPrompt = true )
2014-03-14 14:13:41 -04:00
{
try
{
2014-11-07 15:46:06 -05:00
if ( ! String . IsNullOrEmpty ( CertificateFilename ) | | ShowOpenFileDialog ( CertificatesFilter , "Choose a code signing certificate to import" , "" , "" , ref ChoosingFilesToInstallDirectory , out CertificateFilename ) )
2014-03-14 14:13:41 -04:00
{
2014-11-07 15:46:06 -05:00
if ( Environment . OSVersion . Platform = = PlatformID . MacOSX | | Environment . OSVersion . Platform = = PlatformID . Unix )
2014-03-14 14:13:41 -04:00
{
2014-11-07 15:46:06 -05:00
// run certtool y to get the currently installed certificates
CertToolData = "" ;
Process CertTool = new Process ( ) ;
CertTool . StartInfo . FileName = "/usr/bin/security" ;
CertTool . StartInfo . UseShellExecute = false ;
CertTool . StartInfo . Arguments = "import \"" + CertificateFilename + "\" -k login.keychain" ;
CertTool . StartInfo . RedirectStandardOutput = true ;
CertTool . OutputDataReceived + = new DataReceivedEventHandler ( OutputReceivedCertToolProcessCall ) ;
CertTool . Start ( ) ;
CertTool . BeginOutputReadLine ( ) ;
CertTool . WaitForExit ( ) ;
if ( CertTool . ExitCode ! = 0 )
{
// todo: provide some feedback that it failed
}
Console . Write ( CertToolData ) ;
2014-03-14 14:13:41 -04:00
}
2014-11-07 15:46:06 -05:00
else
2014-03-14 14:13:41 -04:00
{
2014-11-07 15:46:06 -05:00
// Load the certificate
string CertificatePassword = "" ;
X509Certificate2 Cert = null ;
try
2014-03-14 14:13:41 -04:00
{
Cert = new X509Certificate2 ( CertificateFilename , CertificatePassword , X509KeyStorageFlags . PersistKeySet | X509KeyStorageFlags . Exportable | X509KeyStorageFlags . MachineKeySet ) ;
}
2014-11-07 15:46:06 -05:00
catch ( System . Security . Cryptography . CryptographicException ex )
2014-03-14 14:13:41 -04:00
{
2014-11-07 15:46:06 -05:00
// Try once with a password
if ( PasswordDialog . RequestPassword ( out CertificatePassword ) )
2014-03-14 14:13:41 -04:00
{
2014-11-07 15:46:06 -05:00
Cert = new X509Certificate2 ( CertificateFilename , CertificatePassword , X509KeyStorageFlags . PersistKeySet | X509KeyStorageFlags . Exportable | X509KeyStorageFlags . MachineKeySet ) ;
}
else
{
// User cancelled dialog, rethrow
throw ex ;
2014-03-14 14:13:41 -04:00
}
}
2014-11-07 15:46:06 -05:00
// If the certificate doesn't have a private key pair, ask the user to provide one
2014-11-13 10:36:07 -05:00
if ( ! Cert . HasPrivateKey )
2014-03-14 14:13:41 -04:00
{
2014-11-07 15:46:06 -05:00
string ErrorMsg = "Certificate does not include a private key and cannot be used to code sign" ;
2014-03-14 14:13:41 -04:00
2014-11-07 15:46:06 -05:00
// Prompt for a key pair
2014-11-13 17:30:33 -05:00
if ( MessageBox ( new IntPtr ( 0 ) , "Next, please choose the key pair that you made when generating the certificate request." ,
2014-11-07 15:46:06 -05:00
Config . AppDisplayName ,
2014-11-13 17:30:33 -05:00
0x00000000 | 0x00000040 | 0x00001000 | 0x00010000 ) = = 1 )
2014-11-07 15:46:06 -05:00
{
string KeyFilename ;
if ( ShowOpenFileDialog ( KeysFilter , "Choose the key pair that belongs with the signing certificate" , "" , "" , ref ChoosingFilesToInstallDirectory , out KeyFilename ) )
{
Cert = CryptoAdapter . CombineKeyAndCert ( CertificateFilename , KeyFilename ) ;
if ( Cert . HasPrivateKey )
{
ErrorMsg = null ;
}
}
}
if ( ErrorMsg ! = null )
{
throw new Exception ( ErrorMsg ) ;
}
}
// Add the certificate to the store
X509Store Store = new X509Store ( ) ;
Store . Open ( OpenFlags . ReadWrite ) ;
Store . Add ( Cert ) ;
Store . Close ( ) ;
}
2014-03-14 14:13:41 -04:00
}
}
catch ( Exception ex )
{
string ErrorMsg = String . Format ( "Failed to load or install certificate due to an error: '{0}'" , ex . Message ) ;
Program . Error ( ErrorMsg ) ;
2014-11-13 17:30:33 -05:00
System . Threading . Thread . Sleep ( 500 ) ;
MessageBox ( new IntPtr ( 0 ) , ErrorMsg , Config . AppDisplayName , 0x00000000 | 0x00000010 | 0x00001000 | 0x00010000 ) ;
2014-03-14 14:13:41 -04:00
}
}
private void ImportCertificateButton_Click ( object sender , EventArgs e )
{
2014-11-07 15:46:06 -05:00
TryInstallingCertificate_PromptForKey ( null ) ;
2014-03-14 14:13:41 -04:00
UpdateStatus ( ) ;
}
private void EditPlistButton_Click ( object sender , EventArgs e )
{
ConfigureMobileGame Dlg = new ConfigureMobileGame ( ) ;
Dlg . ShowDialog ( this ) ;
UpdateStatus ( ) ;
}
private void CancelThisFormButton_Click ( object sender , EventArgs e )
{
DialogResult = DialogResult . Cancel ;
Close ( ) ;
}
private void ReadyToPackageButton_Click ( object sender , EventArgs e )
{
DialogResult = DialogResult . OK ;
Close ( ) ;
}
private void InstallIPAButton_Click ( object sender , EventArgs e )
{
string PickedFilename ;
if ( ShowOpenFileDialog ( IpaFilter , "Choose an IPA to install" , "ipa" , "" , ref ChoosingIpaDirectory , out PickedFilename ) )
{
Program . ProgressDialog . OnBeginBackgroundWork = delegate
{
DeploymentHelper . InstallIPAOnConnectedDevices ( PickedFilename ) ;
} ;
Program . ProgressDialog . ShowDialog ( ) ;
}
}
private void ResignIPAButton_Click ( object sender , EventArgs e )
{
GraphicalResignTool ResignTool = GraphicalResignTool . GetActiveInstance ( ) ;
ResignTool . TabBook . SelectTab ( ResignTool . ResignPage ) ;
ResignTool . Show ( ) ;
}
private void ProvisionCertToolsButton_Click ( object sender , EventArgs e )
{
2014-08-04 18:29:09 -04:00
// GraphicalResignTool ResignTool = GraphicalResignTool.GetActiveInstance();
// ResignTool.TabBook.SelectTab(ResignTool.ProvisionCertPage);
// ResignTool.Show();
2014-03-14 14:13:41 -04:00
}
private void OtherDeployToolsButton_Click ( object sender , EventArgs e )
{
GraphicalResignTool ResignTool = GraphicalResignTool . GetActiveInstance ( ) ;
ResignTool . TabBook . SelectTab ( ResignTool . DeploymentPage ) ;
ResignTool . Show ( ) ;
}
void OpenHelpWebsite ( )
{
string Target = "http://udn.epicgames.com/Three/AppleiOSProvisioning.html" ;
ProcessStartInfo PSI = new ProcessStartInfo ( Target ) ;
Process . Start ( PSI ) ;
}
private void ToolsHub_HelpButtonClicked ( object sender , CancelEventArgs e )
{
OpenHelpWebsite ( ) ;
}
private void ToolsHub_KeyUp ( object sender , KeyEventArgs e )
{
if ( e . KeyCode = = Keys . F1 )
{
OpenHelpWebsite ( ) ;
}
}
private void HyperlinkClicked ( object sender , LinkLabelLinkClickedEventArgs e )
{
string Target = ( sender as LinkLabel ) . Tag as string ;
ProcessStartInfo PSI = new ProcessStartInfo ( Target ) ;
Process . Start ( PSI ) ;
}
2014-08-01 19:19:55 -04:00
private void InstallIPA_Click ( object sender , EventArgs e )
{
InstallIPAButton_Click ( sender , e ) ;
}
2014-03-14 14:13:41 -04:00
}
}