/// <summary> /// Handle the decryption of the identified filepath to an output directory /// </summary> /// <returns>Returns true of the operation was successful</returns> public override bool StartOperation() { //Check that there is a single file to process if (identifiedFiles.Count != 1) { Console.WriteLine($"Can't start decryption operation, there is an invalid number ({identifiedFiles.Count}) of files set as the target. Expected only 1"); return(false); } //Store the buffers that will be used for the operation FileStream fStream = null; GZipStream reader = null; FileStream writer = null; //Create the encryption key that will be used for this operation VigenèreCipherKey key = new VigenèreCipherKey(Key); //Flag if the operation was completed successfully bool successful = true; //Store a progress value that can be output float progress = 0f; //Store a collection of the files that are generated by this operation //<Temp Path, Final Path> List <Tuple <string, string> > generatedFiles = new List <Tuple <string, string> >(); //Attempt to decrypt the entire file try { //Create the initial buffer objects fStream = new FileStream(TargetPath, FileMode.Open, FileAccess.Read, FileShare.Read, BUFFER_SIZE); reader = new GZipStream(fStream, CompressionMode.Decompress); //Create the byte buffers that will be used for the operation byte[] dataBuffer = new byte[BUFFER_SIZE]; byte[] intBuffer = new byte[sizeof(int)]; byte[] longBuffer = new byte[sizeof(long)]; //Read the number of files that are included in this collection reader.Read(intBuffer, 0, sizeof(int)); key.Decrypt(ref intBuffer, sizeof(int)); //Decrypt the count if (BitConverter.IsLittleEndian) { Array.Reverse(intBuffer, 0, sizeof(int)); } int fileCount = BitConverter.ToInt32(intBuffer, 0); //Calculate the percentage to use for each file processed float percentageUsage = .75f / fileCount; //Loop through each file to be processed for (int i = 0; i < fileCount; i++) { //Read the bytes for the number of characters in the filepath reader.Read(intBuffer, 0, sizeof(int)); key.Decrypt(ref intBuffer, sizeof(int)); //Get the number of characters that are in the if (BitConverter.IsLittleEndian) { Array.Reverse(intBuffer, 0, sizeof(int)); } int characterCount = BitConverter.ToInt32(intBuffer, 0); //Construct the relative filepath back from the data StringBuilder relativePath = new StringBuilder(characterCount); //Loop through and read the filepath long processedCount = 0; do { //Retrieve the next chunk of characters from datafile int readCount = reader.Read(dataBuffer, 0, Math.Min( (BUFFER_SIZE / sizeof(char)) * sizeof(char), (characterCount - (int)processedCount) * sizeof(char) ) ); //Decrypt the character bytes key.Decrypt(ref dataBuffer, readCount); //Half the count for final processing readCount /= sizeof(char); //Extract the characters from the buffer byte[] charBuffer = new byte[sizeof(char)]; for (int c = 0; c < readCount; c++) { //Get the character bytes from the array Array.Copy(dataBuffer, c * sizeof(char), charBuffer, 0, sizeof(char)); //Convert the byte data back to a character if (BitConverter.IsLittleEndian) { Array.Reverse(charBuffer, 0, sizeof(char)); } relativePath.Append(BitConverter.ToChar(charBuffer, 0)); } //Increase the counter processedCount += readCount; } while (processedCount < characterCount); //Get the amount of data to evaluated by this process reader.Read(longBuffer, 0, sizeof(long)); key.Decrypt(ref longBuffer, sizeof(long)); //Get the amount of data to be processed if (BitConverter.IsLittleEndian) { Array.Reverse(longBuffer, 0, sizeof(long)); } long dataCount = BitConverter.ToInt64(longBuffer, 0); //Get a temp file to store the data at string tempPath = Path.GetTempFileName(); //Add the entry to the monitor list generatedFiles.Add(new Tuple <string, string>( tempPath, Path.Combine(DestinationPath, relativePath.ToString()) )); //Try to create process the contained file try { //Open the temporary file for writing writer = new FileStream(tempPath, FileMode.Append, FileAccess.Write, FileShare.Read, BUFFER_SIZE); //Process all of the data within the file processedCount = 0; do { //Retrieve the next chunk of data to process int readCount = reader.Read(dataBuffer, 0, (int)Math.Min( BUFFER_SIZE, dataCount - processedCount ) ); //If there is data to be read but nothing was read from the file, then something has gone wrong if (readCount == 0 && dataCount - processedCount > 0) { throw new OperationCanceledException($"OperationCanceledException: {dataCount - processedCount} bytes are left to be read but 0 bytes were read. Reached EOF"); } //Increase the counters processedCount += readCount; //Decrypt the buffer and write it to the file key.Decrypt(ref dataBuffer, readCount); writer.Write(dataBuffer, 0, readCount); } while (processedCount < dataCount); } #if DEBUG //Log the exception thrown for debugging purposes catch (Exception exec) { Console.WriteLine($"Decryption failed to process internal file. Writing of included file '{relativePath.ToString()}' failed. ERROR: {exec.Message}"); successful = false; break; } #else //Log general failure on exception. Assume key is wrong catch { Console.WriteLine("Decryption failed to process an internal file. Is the Cipher Key correct?"); successful = false; break; } #endif //Cleanup the file writing finally { if (writer != null) { writer.Dispose(); writer = null; } } //Increment the operation percentage progress += percentageUsage; Console.WriteLine($"\tProgress: {(progress * 100f).ToString("F2") + '%'}"); } } #if DEBUG //Log the exception thrown for debugging purposes catch (Exception exec) { Console.WriteLine($"Decryption failed to process internal file. Is the Cipher Key correct? ERROR: {exec.Message}"); successful = false; } #else //Log general failure on exception. Assume key is wrong catch { Console.WriteLine("Decryption failed to process an internal file. Is the Cipher Key correct?"); successful = false; } #endif finally { //Clear out the file streams if (writer != null) { writer.Dispose(); writer = null; } if (reader != null) { reader.Dispose(); reader = null; } if (fStream != null) { fStream.Dispose(); fStream = null; } //Loop for the duration of the cleanup while (true) { //If the operation failed, delete all of the temp files if (!successful) { //Delete all of the (remaining) temp files for (int i = 0; i < generatedFiles.Count; i++) { try { File.Delete(generatedFiles[i].Item1); } catch { } } //Break from the infinite loop break; } //Otherwise, shift the files to the valid destination else { //Calculate a percentage requirement for each file to shift float percentageUsage = .25f / generatedFiles.Count; //Process all of the files string destinationPath = string.Empty; for (int i = 0; i < generatedFiles.Count; i++) { //Get the next filepath that is to be processed by the data destinationPath = generatedFiles[i].Item2; //Attempt to ensure the directory exists, catching filepaths that are to long bool attempted = false, failed = false; while (true) { try { //Ensure the directory exists Directory.CreateDirectory(Path.GetDirectoryName(destinationPath)); //If gotten this far, path is valid. Exit from the loop break; } //Otherwise, shorten the path catch (PathTooLongException) { //If the fix has already been attempted then nothing we can do if (attempted) { Console.WriteLine($"Failed to setup decrypted file '{Path.GetFileName(destinationPath)}' as the resulting filepath '{destinationPath}' was to long"); successful = false; failed = true; break; } //Otherwise, try to temp fix the positioning of the file else { //Construct a debug string that can be used to explain the resulting operation StringBuilder sb = new StringBuilder($"Failed to setup directory for filepath '{destinationPath}' as the filepath is to long. "); //Shorten the filename to the destination directory with additional information (To prevent copy overwrite) destinationPath = Path.Combine(DestinationPath, $"{Path.GetFileNameWithoutExtension(destinationPath)}_({Path.GetFileNameWithoutExtension(generatedFiles[i].Item1)}){Path.GetExtension(destinationPath)}"); //Add the new path to the message sb.Append($"The new path of '{destinationPath}' has been assigned to try and shorten the final path"); //Log the message Console.WriteLine(sb.ToString()); //Flag as the fix being attempted attempted = true; } } } //If the operation has failed, quit out if (failed) { break; } //Delete the file if it already exists try { File.Delete(destinationPath); } catch { } //Try to move the decrypted file to the final location try { File.Move(generatedFiles[i].Item1, destinationPath); } catch (Exception exec) { Console.WriteLine($"Decryption operation failed to relocate decrypted file to '{destinationPath}'. ERROR: {exec.Message}"); successful = false; break; } //Increment the operation percentage progress += percentageUsage; Console.WriteLine($"\tProgress: {(progress * 100f).ToString("F2") + '%'}"); } //Exit from the loop if the operation was a success if (successful) { break; } } } } //Mark the end of the operation return(successful); }
/// <summary> /// Handle the encryption of the identified filepaths to an output file /// </summary> /// <returns>Returns true of the operation was successful</returns> public override bool StartOperation() { //Check that there are files to process if (identifiedFiles.Count == 0) { Console.WriteLine("Can't start encryption operation. No files were identified for encryption"); return(false); } //Store the buffers that will be used for the operation FileStream fStream = null; GZipStream writer = null; FileStream reader = null; //Create the encryption key that will be used for this operation VigenèreCipherKey key = new VigenèreCipherKey(Key); //Flag if the operation was completed successfully bool successful = true; //Store a progress value that can be output float progress = 0f; //Attempt to parse all of the supplied data try { //Create the initial buffer objects fStream = new FileStream(DestinationPath, FileMode.Create, FileAccess.Write, FileShare.None, BUFFER_SIZE); writer = new GZipStream(fStream, CompressionMode.Compress); //Create the byte buffers that will be used for the operation byte[] dataBuffer = new byte[BUFFER_SIZE]; byte[] intBuffer = null; byte[] longBuffer = null; //Get the root directory of the target path string rootDir = (Path.HasExtension(TargetPath) ? Path.GetDirectoryName(TargetPath) : TargetPath ); //Ensure that there is a trailing slash on the directory if (!rootDir.EndsWith(Path.DirectorySeparatorChar.ToString()) || !rootDir.EndsWith(Path.AltDirectorySeparatorChar.ToString())) { rootDir += Path.DirectorySeparatorChar; } //Determine the percentage to use per file that is encrypted float percentageUsage = 1f / identifiedFiles.Count; //Convert the number of files to process into bytes intBuffer = BitConverter.GetBytes(identifiedFiles.Count); if (BitConverter.IsLittleEndian) { Array.Reverse(intBuffer, 0, sizeof(int)); } key.Encrypt(ref intBuffer, sizeof(int)); //Write the count to the file writer.Write(intBuffer, 0, sizeof(int)); //Process each of the files that are to be included foreach (FileInfo file in identifiedFiles) { //Remove the root directory from the filepath string relativePath = file.FullName.Substring(rootDir.Length); //Get the number of characters to be processed in the relative path intBuffer = BitConverter.GetBytes(relativePath.Length); if (BitConverter.IsLittleEndian) { Array.Reverse(intBuffer, 0, sizeof(int)); } key.Encrypt(ref intBuffer, sizeof(int)); //Write the character count to the file writer.Write(intBuffer, 0, sizeof(int)); //Write the filepath to the buffer long processedCount = 0; do { //Track the number of characters that have been added to the buffer int dataBufferUsage = 0; //Copy the available characters to the buffer for (; processedCount < relativePath.Length && dataBufferUsage + sizeof(char) < BUFFER_SIZE; processedCount++, dataBufferUsage += sizeof(char)) { //Get the bytes for the characters byte[] charBuffer = BitConverter.GetBytes(relativePath[(int)processedCount]); //Copy the characters to the buffer if (BitConverter.IsLittleEndian) { Array.Reverse(charBuffer, 0, sizeof(char)); } Array.Copy(charBuffer, 0, dataBuffer, dataBufferUsage, sizeof(char)); } //Encrypt the buffer and write it to the file key.Encrypt(ref dataBuffer, dataBufferUsage); writer.Write(dataBuffer, 0, dataBufferUsage); } while (processedCount < relativePath.Length); //Get the amount of data in the file longBuffer = BitConverter.GetBytes(file.Length); if (BitConverter.IsLittleEndian) { Array.Reverse(longBuffer, 0, sizeof(long)); } key.Encrypt(ref longBuffer, sizeof(long)); //Write the data count to the buffer writer.Write(longBuffer, 0, sizeof(long)); //Try to open and process the file that is being processed try { //Open the file for reading reader = new FileStream(file.FullName, FileMode.Open, FileAccess.Read, FileShare.Read, BUFFER_SIZE); //Process all of the data in the file processedCount = 0; do { //Retrieve the chunk of data from the file int readCount = reader.Read(dataBuffer, 0, BUFFER_SIZE); //Add to the counter processedCount += readCount; //Encrypt and write the data key.Encrypt(ref dataBuffer, readCount); writer.Write(dataBuffer, 0, readCount); } while (processedCount < file.Length); } //Log any errors that occur catch (Exception exec) { Console.WriteLine($"Encryption failed to process the file '{file.FullName}'. ERROR: {exec.Message}"); successful = false; break; } //Cleanup the file elements finally { if (reader != null) { reader.Dispose(); reader = null; } } //Increment the operation percentage progress += percentageUsage; Console.WriteLine($"\tProgress: {(progress * 100f).ToString("F2") + '%'}"); } } //Catch anything unexpected that happens catch (Exception exec) { Console.WriteLine($"Unexpected error occurred, unable to complete encryption operation. ERROR: {exec.Message}"); successful = false; } finally { //Clear out the file streams if (reader != null) { reader.Dispose(); reader = null; } if (writer != null) { writer.Dispose(); writer = null; } if (fStream != null) { fStream.Dispose(); fStream = null; } //If the operation wasn't successful, delete the file if it exists if (!successful) { File.Delete(DestinationPath); } } //Mark the end of the operation return(successful); }