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.Security.Permissions;
using System.IO;
using SelfUpdatingSettings;
using SelfUpdatingSettings.Classes;
using System.Drawing.Drawing2D;
using System.Security.Cryptography;
using ICSharpCode.SharpZipLib.Zip;
namespace SelfUpdatingPatcher
{
public partial class PatcherDialogBox : Form
{
public PatcherDialogBox()
{
InitializeComponent();
try
{
// Ensure application is only run local to web by just checking for local access to directory
DirectoryInfo r = new DirectoryInfo(Updater.Default.RepositoryExistsPath);
if (r.Exists)
{
DialogResult = System.Windows.Forms.DialogResult.OK;
}
else
{
MessageBox.Show("Repository Cannot Run Outside of Servers, need local access to " + Updater.Default.RepositoryExistsPath,
"Failed to Run", MessageBoxButtons.OK);
this.DialogResult = System.Windows.Forms.DialogResult.Cancel;
}
}
catch (Exception exd)
{
MessageBox.Show("Repository Cannot Run Outside of Servers, need local access to " + Updater.Default.RepositoryExistsPath,
"Failed to Run", MessageBoxButtons.OK);
MessageBox.Show(exd.StackTrace, exd.Message, MessageBoxButtons.OK);
this.DialogResult = System.Windows.Forms.DialogResult.Cancel;
}
}
#region Form-centric events
private void PatcherDialogBox_Load(object sender, EventArgs e)
{
// Refuse to open form if there's a problem
if (this.DialogResult == System.Windows.Forms.DialogResult.Cancel)
{
this.Close();
}
try
{
initFileTable();
obtainSettingValues();
webBrowser.Visible = false;
webBrowser.SetBounds(listResults.Bounds.X, listResults.Bounds.Y, listResults.Bounds.Width, listResults.Bounds.Height);
}
catch (Exception ex)
{
listResults.Items.Add(ex.Message + "\r\n" + ex.StackTrace.ToString());
}
versionUtil = new AssemblyInfoUtil(Updater.Default.VersionFile);
version1Tbox.Value = versionUtil.Version1;
version2Tbox.Value = versionUtil.Version2;
version3Tbox.Value = versionUtil.Version3;
version4Tbox.Value = versionUtil.Version4;
version1Tbox.ValueChanged += versionTbox_ValueChanged;
version2Tbox.ValueChanged += versionTbox_ValueChanged;
version3Tbox.ValueChanged += versionTbox_ValueChanged;
version4Tbox.ValueChanged += versionTbox_ValueChanged;
}
private void PatcherDialogBox_Paint(object sender, PaintEventArgs e)
{
Rectangle BaseRectangle =
new Rectangle(0, 0, this.Width - 1, this.Height - 1);
Brush Gradient_Brush =
new LinearGradientBrush(
BaseRectangle,
Color.DarkOrange, Color.LightSlateGray,
LinearGradientMode.Vertical);
e.Graphics.FillRectangle(Gradient_Brush, BaseRectangle);
}
private void PatcherDialogBox_Resize(object sender, EventArgs e)
{
// Invalidate, or last rendered image will just be scaled
// to new size
this.Invalidate();
}
#endregion
#region Button-centric events
private void btnCancel_Click(object sender, EventArgs e)
{
DialogResult = System.Windows.Forms.DialogResult.Cancel;
this.Close();
}
private void btnBeta_Click(object sender, EventArgs e)
{
listResults.Items.Add("Clicked Beta to Push");
btnBeta.BackColor = Color.LightGreen;
btnBeta.Refresh();
btnProduction.BackColor = DefaultBackColor;
btnProduction.Refresh();
makeListResultsVisible();
versionUtil.WriteFile();
executePushToRepository(ExecuteModeEnum.Beta);
}
private void btnProduction_Click(object sender, EventArgs e)
{
listResults.Items.Add("Clicked Production to Push");
DialogResult dialogResult = MessageBox.Show("Confirm patching of PRODUCTION?", "Patcher Question", MessageBoxButtons.OKCancel, MessageBoxIcon.Exclamation);
if (dialogResult == DialogResult.OK)
{
btnBeta.BackColor = DefaultBackColor;
btnBeta.Refresh();
btnProduction.BackColor = Color.LightGreen;
btnProduction.Refresh();
makeListResultsVisible();
versionUtil.WriteFile();
executePushToRepository(ExecuteModeEnum.Production);
if (MessageBox.Show(this, "Beta will now be pushed to ensure uniformity.", "Pushing Beta", MessageBoxButtons.OKCancel) ==
System.Windows.Forms.DialogResult.OK)
btnBeta_Click(null, null);
}
else
{
listResults.Items.Add("Production Push not Confirmed");
}
}
private void btnViewRepositorySite_Click(object sender, EventArgs e)
{
Cursor.Current = Cursors.WaitCursor;
if (btnViewRepositorySite.Tag == null)
{
listResults.Visible = false;
webBrowser.Visible = true;
webBrowser.SetBounds(listResults.Bounds.X, listResults.Bounds.Y, listResults.Bounds.Width, listResults.Bounds.Height);
webBrowser.Navigate(Updater.Default.UriRepositoryDefault);
btnViewRepositorySite.Text = "Hide &Repository Site";
btnViewRepositorySite.Tag = "Hide";
btnViewRepositorySite.BackColor = Color.PaleTurquoise;
btnViewRepositorySite.Refresh();
btnViewTempFolder.BackColor = DefaultBackColor;
btnViewTempFolder.Refresh();
}
else
{
makeListResultsVisible();
}
Cursor.Current = Cursors.Default;
}
private void btnViewTempFolder_Click(object sender, EventArgs e)
{
btnViewRepositorySite.BackColor = DefaultBackColor;
btnViewRepositorySite.Refresh();
btnViewTempFolder.BackColor = Color.PaleTurquoise;
btnViewTempFolder.Refresh();
OpenFileDialog openFileDialog = new OpenFileDialog();
openFileDialog.InitialDirectory = pathTempCompany;
openFileDialog.Filter = "All files (*.*)|*.*";
openFileDialog.ShowDialog();
}
#endregion
#region Form-level variables
private string appFolder = string.Empty;
private string companyName = string.Empty;
private string repositoryFolder = string.Empty;
private string launcherFolder = string.Empty;
private string setupFolder = string.Empty;
private string softwareManifestFileName = string.Empty;
private DirectoryInfo projReleaseDirectoryInfo;
private DirectoryInfo projSetupDirectoryInfo;
private DirectoryInfo projLauncherDirectoryInfo;
private DirectoryInfo repositoryInfo;
private string pathTempCompany = string.Empty;
private AssemblyInfoUtil versionUtil = null;
private DataTable fileTable = new DataTable("Files");
#endregion
/// <summary>
/// Mainline Logic to invoke upon clicking of a Production/Beta button
/// </summary>
/// <param name="chosenVersionToPush">Execution Mode</param>
private void executePushToRepository(ExecuteModeEnum chosenVersionToPush)
{
Cursor.Current = Cursors.WaitCursor;
try
{
switch (chosenVersionToPush)
{
case ExecuteModeEnum.Beta:
appFolder = Updater.Default.AppSubFolderBeta;
repositoryFolder = Updater.Default.RepositoryDefault + @"\" + Updater.Default.AppSubFolderBeta;
break;
case ExecuteModeEnum.Production:
appFolder = Updater.Default.AppSubFolderProd;
repositoryFolder = Updater.Default.RepositoryDefault + @"\" + Updater.Default.AppSubFolderProd;
break;
}
initFileTable();
DataSet dataSet = new DataSet("SoftwareManifest");
// Make sure external Repository exists
if (!repositoryInfo.Exists)
repositoryInfo.Create();
// walk through the chosen app folder
// and zip the files therein to the appropriate temp folder
if (projReleaseDirectoryInfo.Exists)
{
if (projSetupDirectoryInfo.Exists)
{
// Make sure temp folder is empty so we don't undo a previous push
DirectoryInfo di = new DirectoryInfo(pathTempCompany);
if (di.Exists)
{
foreach (DirectoryInfo prevDirectoryInfo in di.GetDirectories())
{
if (prevDirectoryInfo.Exists)
prevDirectoryInfo.Delete(true);
}
}
// zip the files in the main application folder
zipFilesInFolder(projReleaseDirectoryInfo.FullName, pathTempCompany + "\\" + appFolder, projReleaseDirectoryInfo, true, true);
listResults.Items.Add(" ");
// zip the files in the setup Folder (msi will be skipped)
zipFilesInFolder(projSetupDirectoryInfo.FullName, pathTempCompany + "\\" + appFolder + "\\" + setupFolder, projSetupDirectoryInfo, true, true);
listResults.Items.Add(" ");
// create and zip the software manifest file
string manifestFile = pathTempCompany + "\\" + appFolder + "\\" + softwareManifestFileName;
if (File.Exists(manifestFile))
{
File.Delete(manifestFile);
}
listResults.Items.Add("Creating Software Manifest File: " + softwareManifestFileName);
listResults.Items.Add(" ");
dataSet.Tables.Add(fileTable);
dataSet.WriteXml(manifestFile);
// Write files to repository folder (cleaned above)
UpdateRepository(repositoryInfo);
listResults.Items.Add("Program Completed...");
listResults.Items.Add(" ");
}
else
{
listResults.Items.Add("ProjSetupFolder| " + projSetupDirectoryInfo.FullName + " does not exist!");
}
}
else
{
listResults.Items.Add("ProjReleaseFolder| " + projReleaseDirectoryInfo.FullName + " does not exist!");
}
}
catch (Exception ex)
{
listResults.Items.Add(ex.Message);
listResults.Items.Add(ex.StackTrace.ToString());
}
listResults.Refresh();
listResults.TopIndex = listResults.Items.Count - 1;
Cursor.Current = Cursors.Default;
}
/// <summary>
/// Clear Repository, Transfer Directorys, then Transfer Files
/// </summary>
/// <param name="repositoryInfo">Repository DirectoryInfo</param>
private void UpdateRepository(DirectoryInfo repositoryInfo)
{
// 1) Create directories in Repository and wipe out any existing data
listResults.Items.Add("Clearing Existing Repository: " + repositoryInfo.FullName);
DirectoryInfo updateDir = new DirectoryInfo(repositoryFolder);
if (updateDir.Exists)
{
listResults.Items.Add(" Deleting Directory and SubDirectories: " + updateDir.FullName);
updateDir.Delete(true);
}
listResults.Items.Add(" ");
listResults.Items.Add("Transferring Directories");
DirectoryInfo workingDir = new DirectoryInfo(pathTempCompany);
TransferDirectories(workingDir, workingDir.FullName);
TransferFiles(workingDir, workingDir.FullName);
}
/// <summary>
/// Move the entire directories and files to the target Repository
/// </summary>
/// <param name="directoryInfo">Source DirectoryInfo</param>
/// <param name="baseDir">The To Directory Target</param>
private void TransferDirectories(DirectoryInfo directoryInfo, string baseDir)
{
DirectoryInfo workingDirectoryInfo;
string workingDir = string.Empty;
foreach (DirectoryInfo di in directoryInfo.GetDirectories())
{
workingDir = Updater.Default.RepositoryDefault + di.FullName.Remove(0, baseDir.Length + 1);
workingDirectoryInfo = new DirectoryInfo(workingDir);
if (!workingDirectoryInfo.Exists)
workingDirectoryInfo.Create();
TransferDirectories(di, baseDir);
listResults.Items.Add(" Transferred Directory: " + di.FullName);
TransferFiles(di, baseDir);
listResults.Items.Add(" Transferred Files from " + di.FullName);
listResults.Items.Add(" ");
listResults.Refresh();
listResults.TopIndex = listResults.Items.Count - 1;
}
}
/// <summary>
/// Transfer files from the DirectoryInfo to the baseDirectory Repository
/// </summary>
/// <param name="directoryInfo">The From Directory to copy files</param>
/// <param name="baseDir">The To Directory target</param>
private void TransferFiles(DirectoryInfo directoryInfo, string baseDir)
{
FileInfo workingFile;
string workingDir = string.Empty;
foreach (FileInfo f in directoryInfo.GetFiles())
{
workingFile = new FileInfo(f.FullName);
workingDir = workingFile.FullName.Remove(0, baseDir.Length + 1);
workingDir = Updater.Default.RepositoryDefault + workingDir;
workingFile.CopyTo(workingDir);
}
}
/// <summary>
/// Create File Table with the appropriate columns
/// </summary>
private void initFileTable()
{
fileTable = new DataTable("Files");
fileTable.Columns.Add(new DataColumn("sourcePathName", typeof(string)));
fileTable.Columns.Add(new DataColumn("sourceFileName", typeof(string)));
fileTable.Columns.Add(new DataColumn("zippedPathName", typeof(string)));
fileTable.Columns.Add(new DataColumn("zippedFileName", typeof(string)));
fileTable.Columns.Add(new DataColumn("md5HashedValue", typeof(string)));
}
/// <summary>
/// Make the listResults listbox visible and set the Text, Tag, and BAckColor of various buttons as needed
/// </summary>
private void makeListResultsVisible()
{
webBrowser.Visible = false;
webBrowser.Navigate(string.Empty);
listResults.Visible = true;
btnViewRepositorySite.Text = "View &Repository Site";
btnViewRepositorySite.Tag = null;
btnViewRepositorySite.BackColor = DefaultBackColor;
btnViewRepositorySite.Refresh();
btnViewTempFolder.BackColor = DefaultBackColor;
btnViewTempFolder.Refresh();
}
/// <summary>
/// Obtain and Manipulate the program's settings from misc. values
/// </summary>
private void obtainSettingValues()
{
string pathTempFolder = string.Empty;
try
{
listResults.Items.Clear();
// By putting the setup in the beta/prod folders it allows you to process a "Beta" setup file without
// touching users in production
setupFolder = Updater.Default.AppSubFolderSetup;
// This is the xml file managing your patches
softwareManifestFileName = Updater.Default.SoftwareManifestFileName;
// local path to your release project
projReleaseDirectoryInfo = new DirectoryInfo(Updater.Default.ProjecctReleaseFolder);
// local path to your Setup Project
projSetupDirectoryInfo = new DirectoryInfo(Updater.Default.SetupReleaseFolder);
// local path to your repository
repositoryInfo = new DirectoryInfo(Updater.Default.RepositoryDefault);
// create folders and zip files
// as needed in a temporary folder
pathTempFolder = Path.GetTempPath();
DirectoryInfo di = new DirectoryInfo(pathTempFolder);
pathTempCompany = pathTempFolder + Updater.Default.CompanyName;
di = new DirectoryInfo(pathTempCompany);
if (di.Exists)
{
di.Delete(true);
}
di.Create();
}
catch (Exception ex)
{
listResults.Items.Add(ex.Message + "\r\n" + ex.StackTrace.ToString());
}
}
/// <summary>
/// Zip all of the files in the source folder to the specified temporary folder deriving MD5 hash values along the way, saving them to a datatable for later usage in outputting the manifest XML file.
/// </summary>
/// <param name="sourceFolderFullName">Source Folder Full Name</param>
/// <param name="tempFolder">Temporary Folder Path</param>
/// <param name="directoryInfo">DirectoryInfo object</param>
/// <param name="omitVsHostExecutables">Whether to omit unneeded Visual Studio files</param>
/// <param name="dontZipInstallers">Whether or not to zip the MSI file</param>
private void zipFilesInFolder(string sourceFolderFullName, string tempFolder, DirectoryInfo directoryInfo, bool omitVsHostExecutables, bool dontZipInstallers)
{
string targetSubFolderName = directoryInfo.FullName.Substring(sourceFolderFullName.Length);
while (targetSubFolderName.StartsWith("\\"))
{
targetSubFolderName = targetSubFolderName.Substring(1, targetSubFolderName.Length - 1);
}
string targetFolderName = tempFolder + "\\" + targetSubFolderName;
while (targetFolderName.EndsWith("\\"))
{
targetFolderName = targetFolderName.Substring(0, targetFolderName.Length - 1);
}
// zip each file/sub-folder in the present folder
foreach (FileInfo fi in directoryInfo.GetFiles())
{
// Get MD5 Hash
string md5Hash = string.Empty;
using (FileStream oStream = System.IO.File.OpenRead(fi.FullName))
{
byte[] obuffer = new byte[oStream.Length];
oStream.Read(obuffer, 0, obuffer.Length);
byte[] hashValue = new MD5CryptoServiceProvider().ComputeHash(obuffer);
md5Hash = BitConverter.ToString(hashValue).ToLower();
oStream.Close();
}
// if .vshost executables are allowed
// or the filename does not contain "vshost"
// zip or copy the file as needed
if (!omitVsHostExecutables || !fi.Name.ToLower().Contains("vshost"))
{
// if MSI installers are not to be zipped
// then copy them unmolested to the target folder
if (dontZipInstallers && fi.Name.ToLower().EndsWith(".msi"))
{
string tgtFileName = targetFolderName + "\\" + fi.Name;
if (File.Exists(tgtFileName))
{
File.Delete(tgtFileName);
}
FileInfo tgtFileInfo = new FileInfo(tgtFileName);
if (!tgtFileInfo.Directory.Exists)
{
Directory.CreateDirectory(tgtFileInfo.Directory.FullName);
}
fi.CopyTo(tgtFileName);
addFileRow(tgtFileInfo.Directory.FullName.Substring(pathTempCompany.Length), fi.Name,
tgtFileInfo.Directory.FullName.Substring(pathTempCompany.Length), tgtFileInfo.Name,
md5Hash, 0);
}
//otherwise
else
{
listResults.Items.Add("Zipping file " + fi.FullName);
string zipFileName = targetFolderName + "\\" + fi.Name + ".zip";
listResults.Items.Add(" into " + zipFileName + "...");
listResults.Items.Add(" ");
listResults.Refresh();
listResults.TopIndex = listResults.Items.Count - 1;
if (File.Exists(zipFileName))
{
File.Delete(zipFileName);
}
FileInfo zipFileInfo = new FileInfo(zipFileName);
if (!zipFileInfo.Directory.Exists)
{
Directory.CreateDirectory(zipFileInfo.Directory.FullName);
}
using (ZipOutputStream oZipStream = new ZipOutputStream(File.Create(zipFileName)))
{
oZipStream.Password = Updater.Default.ZipFilePassword;
oZipStream.PutNextEntry(new ZipEntry(fi.Name));
FileStream oStream = File.OpenRead(fi.FullName);
byte[] obuffer = new byte[oStream.Length];
oStream.Read(obuffer, 0, obuffer.Length);
addFileRow(zipFileInfo.Directory.FullName.Substring(pathTempCompany.Length), fi.Name,
zipFileInfo.Directory.FullName.Substring(pathTempCompany.Length), zipFileInfo.Name,
md5Hash);
oZipStream.Write(obuffer, 0, obuffer.Length);
oZipStream.Finish();
oZipStream.Close();
oStream.Close();
}
}
}
}
foreach (DirectoryInfo di in directoryInfo.GetDirectories())
{
zipFilesInFolder(sourceFolderFullName, tempFolder, di, omitVsHostExecutables, dontZipInstallers);
}
}
private void addFileRow(string sourcePathName, string sourceFileName, string zippedPathName, string zippedFileName,
string md5hashedValue, int insertLoc = -1)
{
DataRow dataRow = fileTable.NewRow();
dataRow["sourcePathName"] = sourcePathName;
dataRow["sourceFileName"] = sourceFileName;
dataRow["zippedPathName"] = zippedPathName;
dataRow["zippedFileName"] = zippedFileName;
dataRow["md5hashedValue"] = md5hashedValue;
if (insertLoc > -1)
fileTable.Rows.InsertAt(dataRow, insertLoc);
else
fileTable.Rows.Add(dataRow);
}
/// <summary>
/// Update AssemblyInfoUtil class Version Numbers
/// </summary>
private void versionTbox_ValueChanged(object sender, EventArgs e)
{
if (!versionUtil.fileisDirty)
versionUtil.fileisDirty = true;
if (version1Tbox.Value != versionUtil.Version1)
versionUtil.Version1 = (int)(version1Tbox.Value);
if (version2Tbox.Value != versionUtil.Version2)
versionUtil.Version2 = (int)(version2Tbox.Value);
if (version3Tbox.Value != versionUtil.Version3)
versionUtil.Version3 = (int)(version3Tbox.Value);
if (version4Tbox.Value != versionUtil.Version4)
versionUtil.Version4 = (int)(version4Tbox.Value);
}
}
}