//A worker for Compare mode private static async Task doWork(MinSizeQueue <KeyValuePair <string, string> > queue, IDictionary <string, string> creds, string obj, ICryptoTransform cryptoTrans, TextWriter writer) { SynchronizedIds psid = new SynchronizedIds(); int currentId; Guid guid = Guid.NewGuid(); Trace.TraceInformation($"A worker {guid} has started."); while (true) { currentId = psid.GetCurrentID(); KeyValuePair <string, string> att; HttpResponseMessage response = null; if (minSizeQueue.TryDequeue(out att)) { byte[] valueBytes = null; try { valueBytes = Convert.FromBase64String(att.Value); } catch { Trace.TraceError($"Error decoding Base64 value for {att.Key}"); continue; } if (valueBytes.Length > 0) { using (MemoryStream stream = new MemoryStream(Convert.FromBase64String(att.Value))) { using (CryptoStream cstream = new CryptoStream(stream, cryptoTrans, CryptoStreamMode.Read)) { byte[] decrypted = new byte[valueBytes.Length]; try { int bytesRead = cstream.Read(decrypted, 0, valueBytes.Length); if (bytesRead > 0) { string decryptedValue = Convert.ToBase64String(decrypted); try { response = await ReadFromSalesForce(new Uri(creds["serverUrl"] + "/sobjects/" + obj + "/" + att.Key + "/Body"), creds, HttpMethod.Get, null); } catch (Exception ex) { Trace.TraceError("An error occured while working in Compare mode.\n" + ex.Message); } if (response != null && response.Content != null && response.StatusCode == HttpStatusCode.OK) { using (MemoryStream ms = new MemoryStream()) { response.Content.ReadAsStreamAsync().Result.CopyTo(ms); byte[] res = ms.ToArray(); Array.Resize <byte>(ref decrypted, res.Length); if (res.SequenceEqual(decrypted)) { Trace.TraceInformation($"#{currentId} - {att.Key} is Equal from {guid}."); writer.WriteLine(att.Key + ",EQ"); } else { Trace.TraceInformation($"#{currentId} - {att.Key} is OK from {guid}."); } } } else { Trace.TraceWarning($"#{currentId} - {att.Key} failed to read from {guid}."); writer.WriteLine(att.Key + ",SF_ERROR"); } } } catch (Exception ex) { Trace.TraceError($"{ex.Message}\noccured while trying to compare {att.Key} from {guid}"); } } } } } else { minSizeQueue.Close(); break; } } Trace.TraceInformation($"A worker {guid} has finished the work."); }
static async Task Main(string[] args) { int result = Parser.Default.ParseArguments <Options>(args) .MapResult( (Options opt) => { domainName = opt.SalesForceDomain; groupName = opt.GroupName; entryName = opt.EntryName; pathToKeePassDb = opt.KDBXPath; objectWithAttachments = Enum.GetName(typeof(SFObjectsWithAttachments), opt.SFObject); resultFileName = opt.EcryptedAttachmentsTargetFile == null ? "encrypted_" + objectWithAttachments + ".dat" : opt.EcryptedAttachmentsTargetFile; workingMode = opt.WorkMode; numberOfThreads = opt.NumberOfWorkingThreads; if (opt.LogFilePath != null && !opt.LogFilePath.Equals(String.Empty)) { Trace.Listeners.Add(new TextWriterTraceListener(opt.LogFilePath, "Backup_fileTracer")); Trace.Listeners["Backup_fileTracer"].TraceOutputOptions |= TraceOptions.DateTime; } if (opt.LogToConsole != 0) { Trace.Listeners.Add(consoleTraceListener); } Trace.AutoFlush = true; Trace.Listeners.Remove("Default"); if (workingMode == WorkingModes.Compare && (opt.ComparisonResultsFilePath == null || opt.ComparisonResultsFilePath.Equals(String.Empty))) { Trace.TraceError($"If workmode is set to compare then comparison file must be provided."); WaitExitingCountdown(waittime); Environment.Exit(-1); return(0); } else { pathToComparisonResults = opt.ComparisonResultsFilePath; if (workingMode == WorkingModes.Read) { filter = "+WHERE+" + opt.ReadModeFilter; } } Trace.TraceInformation("Arguments have been successfully parsed"); return(1); }, (IEnumerable <Error> errs) => { WaitExitingCountdown(waittime); Environment.Exit(-1); return(0); }); SecureString securePwd = new SecureString(); Console.Write("Enter password for KeePass: "******"*"); } else { if (securePwd.Length > 0) { securePwd.RemoveAt(securePwd.Length - 1); Console.Write("\b \b"); } } // Exit if Enter key is pressed. } while (key.Key != ConsoleKey.Enter); Console.WriteLine(); Dictionary <string, ProtectedString> credentialsDict = new Dictionary <string, ProtectedString>(OpenKeePassDB(securePwd)); Trace.TraceInformation($"Got {credentialsDict.Count} credentials"); if (credentialsDict.Where(t => t.Key == "IV" || t.Key == "AESPass" || t.Key == "Salt").Count() < 3) { Trace.TraceError("Necessary cryptographic input is absent in the provided entry in the KDBX."); WaitExitingCountdown(waittime); return; } else if (workingMode == WorkingModes.Prepare) { Trace.TraceInformation("Preparation of KDBX has been successfully completed"); WaitExitingCountdown(waittime); return; } Dictionary <string, string> salesForceSID = new Dictionary <string, string>(await GetSalesForceSessionId(credentialsDict)); if (salesForceSID.Count == 0) { Trace.TraceError("Error getting SalesForce session ID. Exiting..."); WaitExitingCountdown(waittime); return; } #region ChecksOfNeededFiles switch (workingMode) { case WorkingModes.Read: listOfIds = (await GetListOfIds(salesForceSID, objectWithAttachments)).ToList(); if (listOfIds.Count != 0) { Trace.TraceInformation($"Got {listOfIds.Count} Ids in the {objectWithAttachments} object"); } else { Trace.TraceError("Nothing to extract. Exiting..."); WaitExitingCountdown(waittime); Environment.Exit(-2); } break; case WorkingModes.Write: case WorkingModes.Compare: if (!File.Exists(resultFileName)) { Trace.TraceError("Source file does not exist. Exiting..."); WaitExitingCountdown(waittime); Environment.Exit(-3); } break;; } #endregion #region CryptographicStuff SymmetricAlgorithm cipher = SymmetricAlgorithm.Create("AesManaged"); cipher.Mode = CipherMode.CBC; cipher.Padding = PaddingMode.PKCS7; cipher.IV = Convert.FromBase64String(credentialsDict["IV"].ReadString()); Byte[] passwordKey = NewPasswordKey(SecureStringExtension.ToSecureString(credentialsDict["AESPass"].ReadString()), credentialsDict["Salt"].ReadString()); #endregion #region StartWorkers List <Task> tasks = new List <Task>(); switch (workingMode) { case WorkingModes.Read: using (TextWriter resultStream = TextWriter.Synchronized(new StreamWriter(resultFileName, false, Encoding.ASCII))) { Trace.TraceInformation($"Initiating {numberOfThreads} workers to read data."); for (int i = 0; i < numberOfThreads; i++) { tasks.Add(Task.Run( () => doWork(listOfIds.ToList(), salesForceSID, objectWithAttachments, cipher.CreateEncryptor(passwordKey, cipher.IV), resultStream))); } Task.WaitAll(tasks.ToArray()); } break; case WorkingModes.Write: minSizeQueue = new MinSizeQueue <KeyValuePair <string, string> >(numberOfThreads); _ = FillQueue(); Trace.TraceInformation($"Initiating {numberOfThreads} workers to write data."); for (int i = 0; i < numberOfThreads; i++) { tasks.Add(Task.Run( () => doWork(minSizeQueue, salesForceSID, objectWithAttachments, cipher.CreateDecryptor(passwordKey, cipher.IV)))); } Task.WaitAll(tasks.ToArray()); break; case WorkingModes.Compare: minSizeQueue = new MinSizeQueue <KeyValuePair <string, string> >(numberOfThreads); _ = FillQueue(); using (TextWriter resultStream = TextWriter.Synchronized(new StreamWriter(pathToComparisonResults, false, Encoding.ASCII))) { Trace.TraceInformation($"Initiating {numberOfThreads} workers to compare data."); for (int i = 0; i < numberOfThreads; i++) { tasks.Add(Task.Run( () => doWork(minSizeQueue, salesForceSID, objectWithAttachments, cipher.CreateDecryptor(passwordKey, cipher.IV), resultStream))); } Task.WaitAll(tasks.ToArray()); } break; } Trace.TraceInformation("All threads complete"); WaitExitingCountdown(waittime); }
//A worker for Write mode private static async Task doWork(MinSizeQueue <KeyValuePair <string, string> > queue, IDictionary <string, string> creds, string obj, ICryptoTransform cryptoTrans) { Guid guid = Guid.NewGuid(); Trace.TraceInformation($"A worker {guid} has started."); HttpResponseMessage response = null; while (true) { KeyValuePair <string, string> att; if (minSizeQueue.TryDequeue(out att)) { byte[] valueBytes = null; try { valueBytes = Convert.FromBase64String(att.Value); } catch { Trace.TraceError($"Error decoding Base64 value for {att.Key}"); continue; } if (valueBytes.Length > 0) { //MemoryStream to store decrypted body to using (MemoryStream stream = new MemoryStream(Convert.FromBase64String(att.Value))) { using (CryptoStream cstream = new CryptoStream(stream, cryptoTrans, CryptoStreamMode.Read)) { byte[] decrypted = new byte[valueBytes.Length]; try { int bytesRead = cstream.Read(decrypted, 0, valueBytes.Length); if (bytesRead > 0) { string decryptedValue = Convert.ToBase64String(decrypted); string json = "{\"Body\":\"" + decryptedValue + "\"}"; try { response = await ReadFromSalesForce(new Uri(creds["serverUrl"] + "/sobjects/" + obj + "/" + att.Key), creds, new HttpMethod("PATCH"), json); } catch (Exception ex) { Trace.TraceError("An exception occured while working in write mode.\n" + ex.Message); } if (response != null && response.Content != null && response.StatusCode == HttpStatusCode.OK) { Trace.TraceInformation($"{att.Key} has been successfully updated by {guid}."); } else if (response.StatusCode == HttpStatusCode.NoContent) { Trace.TraceInformation($"{att.Key}'s content has obviously been modified by {guid}, though \"no content\" has been returned."); } else { Trace.TraceError($"{att.Key} failed to update by {guid}. {response?.StatusCode}"); } } } catch (Exception ex) { Trace.TraceError($"{ex.Message}\noccured while trying to update {att.Key} from {guid}"); } } } } else { Trace.TraceError($"{att.Key} didn't give any body for writing."); } } else { minSizeQueue.Close(); break; } } Trace.TraceInformation($"A worker {guid} has finished the work."); }