using System; using System.Collections.Generic; using System.Text; using System.IO; using System.Data; using System.Threading; using System.Text.RegularExpressions; namespace Bookings_And_Loans.Database { /// /// This file will handle the backup aspect of the system /// class csBackupSystem { //Declare the variables I will be using within this file private static bool isJournalEnabled = true; private static string backupLocation_; public static string backupLocation { //When accessed, return the value of backupLocation_ get { return backupLocation_; } set { //When attempting to set the value of this variable, move all existing backups to the new folder if (backupLocation_!= null) { MoveAllExistingBackups(backupLocation_, value); } //Set the journal file to the new location journalFile = value + "\\journal.txt"; //Save the settings Properties.Settings.Default.BackupLocation = value; Properties.Settings.Default.Save(); backupLocation_ = value; } } private static string journalFile; public static void Initialize() { //A method to start all aspects directly related to the backups system backupLocation = Properties.Settings.Default.BackupLocation; if (!Directory.Exists(backupLocation)) { Directory.CreateDirectory(backupLocation); } //Add the eventhandlers for each of the tables to be covered by the backup system for (int repeat = 0; repeat < csDatabase.dbBookings.Tables.Count; repeat++) { csDatabase.dbBookings.Tables[repeat].RowChanged += new System.Data.DataRowChangeEventHandler(csBackupSystem_RowChanged); csDatabase.dbBookings.Tables[repeat].RowDeleting += new System.Data.DataRowChangeEventHandler(csBackupSystem_RowChanged); csDatabase.dbBookings.Tables[repeat].TableCleared += new DataTableClearEventHandler(csBackupSystem_TableCleared); } //Call the procedure which will check if a new backup is required CheckBackup(); } static void csBackupSystem_TableCleared(object sender, DataTableClearEventArgs e) { //Create a string to reflect that a table was cleared within the database string lineToLog; lineToLog = e.Table.TableName + "><" + "Clear" + "><" + "%%"; LogChange(lineToLog); } private static void CheckBackup() { //Start a new thread, to avoid making the program unresponsive while checking/backing up new Thread(delegate() { //Create an unending loop, which will pause for 12hours after each run through while (true) { //Get a list of backups and find the latest one string[] backupFolders = Directory.GetDirectories(backupLocation); string latestBackup = DateTime.Today.AddDays(-8).ToString("yyyy-MM-dd"); if (backupFolders.Length > 0) { latestBackup = backupFolders[backupFolders.Length - 1].Split('\\')[backupFolders[backupFolders.Length - 1].Split('\\').Length-1]; } DateTime dateOfLastBackup = DateTime.Parse(latestBackup); //Compare todays date to the last backup, and if the last backup was seven days ago, make a new backup if (DateTime.Compare(dateOfLastBackup.AddDays(7), DateTime.Today).Equals(-1)) { CreateBackup(); } int nextCheckInterval = (((60 * 1000) * 60) * 12); Thread.Sleep(nextCheckInterval); } }) { IsBackground = true }.Start(); } private static void CreateBackup() { //Create the directory to backup to and get a list of files to backup Directory.CreateDirectory(backupLocation + "\\" + DateTime.Today.ToString("yyyy-MM-dd")); string[] filesToBackup = new string[] { "bookings.txt", "loans.txt", "archived_loans,txt", "archived_bookings.txt", "users.xml", "journal.txt" }; //Foreach file to backup, copy it to the backup folder for (int repeat = 0; repeat < filesToBackup.Length; repeat++) { string fileToCopy = filesToBackup[repeat]; if (File.Exists(fileToCopy)) { File.Copy(fileToCopy, backupLocation + "\\" + DateTime.Today.ToString("yyyy-MM-dd") + "\\" + fileToCopy); } } //Delete the journal file as all recorded data is no longer needed File.Delete(journalFile); } private static void csBackupSystem_RowChanged(object sender, System.Data.DataRowChangeEventArgs e) { //If the table being added to is an archive table, set the archive date of the record if (e.Action.ToString().Equals("Add") && e.Row.Table.TableName.StartsWith("archived")) { e.Row["Date Archived"] = DateTime.Now; } string lineToLog; //Create the string which is to be recorded, including table, action and all values after the change in the row lineToLog = e.Row.Table.TableName + "><" + e.Action + "><"; for (int repeat = 0; repeat < e.Row.ItemArray.Length; repeat++) { lineToLog += "%%" + e.Row.ItemArray[repeat]; } //Log the change LogChange(lineToLog); } private static void LogChange(string changeToLog) { //If the journal file is active, write the change to the database if (isJournalEnabled) { StreamWriter journalWriter = File.AppendText(journalFile); journalWriter.WriteLine(changeToLog); journalWriter.Flush(); journalWriter.Close(); } } public static void RestoreLastBackup() { //Find the latest backup and restore it string[] backupFolders = Directory.GetDirectories(backupLocation); string latestBackup = DateTime.Today.AddDays(-8).ToString("dd-MM-yyyy"); if (backupFolders.Length > 0) { latestBackup = backupFolders[backupFolders.Length - 1].Split('\\')[1]; } RestoreBackup(latestBackup); } public static void RestoreBackup(string folderToUse) { //Make sure the journal doesn't log the changes made when restoring the backup isJournalEnabled = false; csDatabase.ClearAllTables(); string[] filesToRestore = new string[] { "bookings.txt", "loans.txt", "archived_loans,txt", "archived_bookings.txt", "users.xml" }; for (int repeat = 0; repeat < filesToRestore.Length; repeat++) { string fileToRestore = filesToRestore[repeat]; if (File.Exists(backupLocation + "\\" + folderToUse + "\\" + fileToRestore)) { File.Copy(backupLocation + "\\" + folderToUse + "\\" + fileToRestore, fileToRestore, true); csDatabase.LoadTable(fileToRestore.Split('.')[0]); } } //Restore all changes recorded in the journal RestoreJournal(); isJournalEnabled = true; } private static void RestoreJournal() { //If the journal file exists, continue if (File.Exists(journalFile)) { StreamReader journalReader = new StreamReader(journalFile); string line = ""; while ((line = journalReader.ReadLine()) != null) { try { //Set the variables which will hold the data required by the system to restore each change string tableToAffect = Regex.Split(line, "><")[0]; string actionToPerform = Regex.Split(line, "><")[1]; string[] fieldValues = Regex.Split(Regex.Split(line, "><")[2], "%%"); string idColumn = csDatabase.dbBookings.Tables[tableToAffect].Columns[0].ColumnName; DataRow rowToEdit; switch (actionToPerform) { case "Change": { rowToEdit = csDatabase.dbBookings.Tables[tableToAffect].Select("[" + idColumn + "] = " + fieldValues[1])[0]; for (int a = 0; a < rowToEdit.Table.Columns.Count; a++) { string columnToEdit = rowToEdit.Table.Columns[a].ColumnName; rowToEdit[columnToEdit] = fieldValues[a + 1]; } break; } case "Add": { rowToEdit = csDatabase.dbBookings.Tables[tableToAffect].NewRow(); for (int a = 0; a < rowToEdit.Table.Columns.Count; a++) { string columnToEdit = rowToEdit.Table.Columns[a].ColumnName; string dataTypeOfColumn = rowToEdit.Table.Columns[a].DataType.ToString(); rowToEdit[columnToEdit] = fieldValues[a + 1]; } csDatabase.dbBookings.Tables[tableToAffect].Rows.Add(rowToEdit); break; } case "Delete": { rowToEdit = csDatabase.dbBookings.Tables[tableToAffect].Select("[" + idColumn + "] = " + fieldValues[1])[0]; csDatabase.dbBookings.Tables[tableToAffect].Rows.Remove(rowToEdit); break; } case "Clear": { csDatabase.dbBookings.Tables[tableToAffect].Clear(); break; } } } catch (Exception e) { } } } } private static void MoveAllExistingBackups(string existingFolder, string newFolder) { //Get a list of existing backups string[] backupFolders = Directory.GetDirectories(existingFolder); if (File.Exists(existingFolder + "\\journal.txt")) { File.Copy(existingFolder + "\\journal.txt", newFolder + "\\journal.txt"); } //For each existing backup for (int repeat = 0; repeat < backupFolders.Length; repeat++) { //Get a list of files and move them to the relevant new directory string[] backupFiles = Directory.GetFiles(backupFolders[repeat]); string folderName = Regex.Split(backupFolders[repeat], "\\\\")[Regex.Split(backupFolders[repeat], "\\\\").Length - 1]; Directory.CreateDirectory(newFolder + "\\" + folderName); for (int repeat_ = 0; repeat_ < backupFiles.Length; repeat_++) { string destination = newFolder + "\\" + Regex.Match(backupFiles[repeat_], "([0-9]*(-)?){3}\\\\[a-z]*\\.[a-z]{3}").Value; File.Copy(backupFiles[repeat_], destination); File.Delete(backupFiles[repeat_]); } //Delete the existing backup Directory.Delete(backupFolders[repeat]); } } } }