patbef-ServiceInside/ServiceInside/Service/BackgroundWorker.cs

1046 lines
44 KiB
C#
Raw Normal View History

2024-01-29 16:26:54 +01:00
using ServiceShared;
using ServiceShared.Crypto;
using ServiceShared.Database;
using ServiceShared.Models.HL7;
using ServiceShared.Models.Response;
namespace ServiceInside.Service
{
public class BackgroundWorker
{
private enum AckTypes { SUBSCRIBE, UNSUBSCRIBE, DELETE_DEVICE, PICKEDUP, ERROR }
/// <summary>
/// Directory path of private files(hl7). This directory must not be accessible by outside service
/// </summary>
private static string _PrivateDirectory;
/// <summary>
/// Ack directory inside of PrivateDirectory(it will be created automatically)
/// </summary>
private static string _PrivateDirectoryAck;
/// <summary>
/// Ack done directory inside of PrivateDirectoryAck(it will be created automatically)
/// </summary>
private static string _PrivateDirectoryAckDone;
/// <summary>
/// Directory path of public files(encrypted pdf). This directory must contain only encrypted files and can be accessed by outside with a valid/authorized request
/// No access over filesystem for outside service
/// </summary>
private static string _PublicDirectory;
/// <summary>
/// Queue of Workers
/// </summary>
private static List<string> WorkerThreadIds = null;
/// <summary>
/// Locks operations on the worker threads
/// </summary>
private static bool _WorkerThreadsLock = false;
/// <summary>
/// Number of max worker threads at same time
/// </summary>
private static int MaxWorkerThreads = 10;
/// <summary>
/// Number of tries to check not found results
/// </summary>
private static int MaxTryNotFoundResults = 2;
/// <summary>
/// Checking interval for not found results
/// </summary>
private static int CheckNotFoundResultsIntervalMinutes = 24;
/// <summary>
/// BackgroundWorker Thread
/// </summary>
private static Thread _workerThread;
/// <summary>
/// BackgroundWroker for missing actions (next try of not found results, not notified completed results)
/// </summary>
private static Thread _workerThreadMissingActions;
/// <summary>
/// Flag if BackgroundWorker is running
/// </summary>
private static bool _Running = false;
/// <summary>
/// Results database controller
/// </summary>
private static ServiceShared.Database.Controllers.Results dbResults;
/// <summary>
/// Sets parameter for BackgroundWorker
/// </summary>
/// <param name="privateDirectory">Directory path of private files(hl7). This directory must be private and outside service must not have access on this path</param>
/// <param name="publicDirectory">Directory path of public files(encrypted pdf). This directory contains encrypted files and can be accessed by outside with a valid/authorized request</param>
/// <param name="maxWorkerThreads">Number of allowed worker threads at the same time</param>
/// <param name="maxTryNotFoundResults">Number of posible tries to check not found results, else sends not found notification to patient</param>
/// <param name="checkNotFoundResultsIntervalMinutes">Timeinterval for checking not found results in minutes</param>
public static void SetParameters(string privateDirectory, string publicDirectory, DbContext dbContext, int maxWorkerThreads = 10, int maxTryNotFoundResults = 2, int checkNotFoundResultsIntervalMinutes = 24)
{
_PrivateDirectory = privateDirectory;
_PublicDirectory = publicDirectory;
MaxWorkerThreads = maxWorkerThreads;
MaxTryNotFoundResults = maxTryNotFoundResults;
CheckNotFoundResultsIntervalMinutes = checkNotFoundResultsIntervalMinutes;
if(CheckNotFoundResultsIntervalMinutes < 5)
{
CheckNotFoundResultsIntervalMinutes = 5;
}
WorkerThreadIds = new List<string>();
dbResults = new ServiceShared.Database.Controllers.Results(dbContext);
try
{
if(!Directory.Exists(_PrivateDirectory))
{
Directory.CreateDirectory(_PrivateDirectory);
}
if (!Directory.Exists(_PublicDirectory))
{
Directory.CreateDirectory(_PublicDirectory);
}
_PrivateDirectoryAck = Path.Combine(_PrivateDirectory, "ack");
if (!Directory.Exists(_PrivateDirectoryAck))
{
Directory.CreateDirectory(_PrivateDirectoryAck);
}
_PrivateDirectoryAckDone = Path.Combine(_PrivateDirectoryAck, "done");
if (!Directory.Exists(_PrivateDirectoryAckDone))
{
Directory.CreateDirectory(_PrivateDirectoryAckDone);
}
}
catch (Exception ex)
{
Log.Error(ex, "ServiceInside.Service.BackgroundWorker", "SetParameters");
}
}
/// <summary>
/// Starts the background working
/// </summary>
public static void Start()
{
try
{
if(_Running)
{
Stop();
}
_workerThread = new Thread(WorkAsync);
_workerThread.Start();
_workerThreadMissingActions = new Thread(CheckMissingActionsAsync);
_workerThreadMissingActions.Start();
}
catch (Exception ex)
{
Log.Critical(ex, "ServiceInside.Service.BackgroundWorker", "Start");
}
}
/// <summary>
/// Stops the background working
/// </summary>
public static void Stop()
{
try
{
_Running = false;
}
catch (Exception ex)
{
Log.Critical(ex, "ServiceInside.Service.BackgroundWorker", "Stop");
}
}
/// <summary>
/// Creates subscribe ack log file for the primary system
/// </summary>
/// <param name="pgs">pgs that was currently created</param>
/// <param name="udid">device id where the pgs was currently created</param>
public static void Subscribe(string pgs, string udid)
{
try
{
if (!string.IsNullOrEmpty(pgs) && !string.IsNullOrEmpty(udid))
{
ServiceShared.Models.Database.Results results = dbResults.GetResults(pgs, udid);
if (results != null && CreateAck(results.UDID, results.PGS, results.PGS_HASH, AckTypes.SUBSCRIBE))
{
Log.Trace(AckTypes.SUBSCRIBE.ToString(), results.PGS, results.UDID);
}
}
}
catch (Exception ex)
{
Log.Critical(ex, "ServiceInside.Service.BackgroundWorker", "Subscribe(string, string)");
}
}
/// <summary>
/// Creates unsubscribe ack log file for the primary system and removes results if already exists from the storages
/// </summary>
/// <param name="pgs">pgs that should be unsubscripted</param>
/// <param name="pgs_hash">pgs aes encrypted value</param>
/// <param name="udid">device id where the pgs was created</param>
public static void Unsubscribe(string pgs, string pgs_hash, string udid)
{
try
{
if (!string.IsNullOrEmpty(pgs) && !string.IsNullOrEmpty(udid))
{
DeleteFilesFor(pgs, udid);
if (CreateAck(udid, pgs, pgs_hash, AckTypes.UNSUBSCRIBE))
{
Log.Trace(AckTypes.UNSUBSCRIBE.ToString(), pgs, udid);
}
}
}
catch (Exception ex)
{
Log.Critical(ex, "ServiceInside.Service.BackgroundWorker", "Unsubscribe(string, string, string)");
}
}
/// <summary>
/// Creates delete device ack log file for the primary system
/// </summary>
/// <param name="udid">udid hash(partial pk)</param>
/// <param name="verificator_hash">verificator_hash, that validate the client permission</param>
/// <returns>returns true if all data of udid have been deleted from the app services</returns>
public static bool DeleteDevice(string udid, string verificator_hash)
{
bool result = false;
try
{
if (!string.IsNullOrEmpty(udid) && !string.IsNullOrEmpty(verificator_hash))
{
ServiceShared.Models.Database.Device device = dbResults.GetDevice(udid);
if(device != null)
{
List<ServiceShared.Models.Database.Results> results = dbResults.GetDeviceResults(udid, verificator_hash);
if (results != null && results.Count > 0)
{
foreach (ServiceShared.Models.Database.Results res in results)
{
DeleteFilesFor(res.PGS, res.UDID);
Log.Trace("device deleted", res.PGS, res.UDID);
}
if (dbResults.DeleteDevice(udid, verificator_hash))
{
results = dbResults.GetDeviceResults(udid, verificator_hash);
result = (results == null || results.Count <= 0);
}
}
else
{
result = true;
}
}
else
{
result = true;
}
if (result && dbResults.DeleteDevice(udid, verificator_hash))
{
CreateAck(udid, null, null, AckTypes.DELETE_DEVICE, (device != null ? device.PatHash : null));
}
}
}
catch (Exception ex)
{
Log.Critical(ex, "ServiceInside.Service.BackgroundWorker", "DeleteDevice(string, string)");
}
return result;
}
/// <summary>
/// Returns encrypted results as download object if it´s already available and has COMPLETED status
/// </summary>
/// <param name="pgs">pgs of results(PK part)</param>
/// <param name="udid">udid of results(PK part)</param>
/// <returns>Returns download object, that contains encrypted results</returns>
public static Download GetDownload(string pgs, string udid)
{
Download result = null;
try
{
if (!string.IsNullOrEmpty(pgs) && !string.IsNullOrEmpty(udid))
{
ServiceShared.Models.Database.Results results = dbResults.GetResults(pgs, udid);
if (results.Available &&
results.Status == ServiceShared.Models.Database.Results.ResultsStatus.COMPLETED &&
!string.IsNullOrEmpty(results.ServerPublicKey))
{
string file = Path.Combine(_PublicDirectory, GetFileName(results.PGS, results.UDID));
if (File.Exists(file))
{
result = new Download();
result.server_public_key = results.ServerPublicKey;
result.encrypted_content = File.ReadAllText(file);
result.pgs = results.PGS;
result.udid = results.UDID;
Log.Trace("get_download", results.PGS, results.UDID);
}
}
}
}
catch (Exception ex)
{
Log.Critical(ex, "ServiceInside.Service.BackgroundWorker", "GetDownload(string, string)");
}
return result;
}
/// <summary>
/// Moves results file to the done or error directory in the public area, depended on successfully pickedup checksum
/// If pickedup was successfully it removes the original results from the private storage
/// </summary>
/// <param name="pgs">pgs of results(PK part)</param>
/// <param name="udid">udid of results(PK part)</param>
/// <param name="file_checksum">checksum of content(base64) after decryption, that was pickedup by patient</param>
public static void SetPickedUpResultsAsync(string pgs, string udid, string file_checksum)
{
try
{
if (!string.IsNullOrEmpty(pgs) && !string.IsNullOrEmpty(udid))
{
Thread thread = new Thread(() =>
{
ServiceShared.Models.Database.Results results = dbResults.GetResults(pgs, udid);
if (results != null &&
results.PickedUp &&
!string.IsNullOrEmpty(results.FileChecksum) &&
results.FileChecksum == file_checksum)
{
string file = Path.Combine(_PublicDirectory, GetFileName(results.PGS, results.UDID));
if (File.Exists(file))
{
File.Delete(file);
}
CreateAck(results.UDID, results.PGS, results.PGS_HASH, AckTypes.PICKEDUP);
DeleteFilesFor(results.PGS, results.UDID);
}
});
thread.Start();
}
}
catch (Exception ex)
{
Log.Critical(ex, "ServiceInside.Service.BackgroundWorker", "SetPickedUpResultsAsync(string, string, string)");
}
}
/// <summary>
/// Checks missing or failed actions like notification on completed results or not found results
/// </summary>
private static void CheckMissingActionsAsync()
{
try
{
_Running = true;
while (_Running)
{
Log.Debug("[BW] check missing actions");
Log.Debug("[BW] check not found results (Interval: " + CheckNotFoundResultsIntervalMinutes + ", MaxTry: " + MaxTryNotFoundResults + ")");
/** check not found results after x Minutes (it should be minimum 5 minutes) **/
List<ServiceShared.Models.Database.Results> notFoundResults = dbResults.GetNotFoundResults(CheckNotFoundResultsIntervalMinutes, MaxTryNotFoundResults);
if(notFoundResults != null && notFoundResults.Count > 0)
{
Log.Debug("[BW] found not found results (" + notFoundResults.Count + ")");
foreach(ServiceShared.Models.Database.Results results in notFoundResults)
{
CreateAck(results.UDID, results.PGS, results.PGS_HASH, AckTypes.SUBSCRIBE);
}
}
/** check not notified completed results **/
List<ServiceShared.Models.Database.Results> complitedNotNotfiedResults = dbResults.GetNotNotifiedResults();
if(complitedNotNotfiedResults != null && complitedNotNotfiedResults.Count > 0)
{
Log.Debug("[BW] found not notified completed results (" + complitedNotNotfiedResults.Count + ")");
foreach (ServiceShared.Models.Database.Results results in complitedNotNotfiedResults)
{
Notification notification = new Notification();
notification.pgs = results.PGS;
notification.udid = results.UDID;
notification.created = DateTime.Now;
notification.status = results.Status;
notification.available = results.Available;
notification.available_ts = results.AvailableTS;
Log.Debug("[BW] try next notification PGS(" + results.PGS + ")");
ServiceOutside.Notify(notification);
}
}
/** check not found & not notificated results **/
List<ServiceShared.Models.Database.Results> notFoundNotNotfiedResults = dbResults.GetNotFoundNotNotifiedResults(MaxTryNotFoundResults);
if (notFoundNotNotfiedResults != null && notFoundNotNotfiedResults.Count > 0)
{
Log.Debug("[BW] found not notified not found results (" + notFoundNotNotfiedResults.Count + ")");
foreach (ServiceShared.Models.Database.Results results in notFoundNotNotfiedResults)
{
Notification notification = new Notification();
notification.pgs = results.PGS;
notification.udid = results.UDID;
notification.created = DateTime.Now;
notification.status = results.Status;
notification.available = false;
notification.available_ts = null;
Log.Debug("[BW] try next notification PGS(" + results.PGS + ") for not found");
ServiceOutside.Notify(notification);
}
}
// Sleep 5 minutes
Thread.Sleep(((1000 * 60) * 5));
}
}
catch (Exception ex)
{
Log.Critical(ex, "ServiceInside.Service.BackgroundWorker", "CheckMissingActionsAsync");
}
}
/// <summary>
/// Works in background
/// </summary>
private static void WorkAsync()
{
try
{
_Running = true;
while(_Running)
{
string[] files = Directory.GetFiles(_PrivateDirectory, "*.hl7");
if (files != null && files.Length > 0)
{
int i = 0;
foreach(string file in files)
{
try
{
if(FileCanBeRead(file))
{
if (i > MaxWorkerThreads)
{
break;
}
string newThreadId = SHA512.Encrypt(file);
if (WorkerThreadIds.Count < MaxWorkerThreads && !WorkerThreadIds.Contains(newThreadId) && !_WorkerThreadsLock)
{
WorkerThreadIds.Add(newThreadId);
ParameterizedThreadStart workerStart = new ParameterizedThreadStart(DoWorkAsync);
Thread workerThread = new Thread(workerStart);
workerThread.Start(new object[] { workerThread, file, newThreadId });
}
i++;
}
}
catch (Exception ex)
{
Log.Critical(ex, "ServiceInside.Service.BackgroundWorker", "WorkAsync(Step)");
}
finally
{
Thread.Sleep(200);
}
}
}
Thread.Sleep(200);
}
}
catch (Exception ex)
{
Log.Critical(ex, "ServiceInside.Service.BackgroundWorker", "WorkAsync");
}
}
/// <summary>
/// Single work with hl7 file from working directory
/// </summary>
/// <param name="args">array of arguments [0] = workerThread, [1] = hl7 file</param>
private static void DoWorkAsync(object args)
{
string file = null;
Thread workerThread = null;
string workerThreadId = null;
try
{
if(args != null)
{
object[] param = (object[])args;
if(param != null && param.Length == 3)
{
workerThread = (Thread)param[0];
file = (string)param[1];
workerThreadId = (string)param[2];
Log.Debug("[BW] File: " + file);
Log.Debug("[BW] ThreadId: " + workerThreadId);
if (!string.IsNullOrEmpty(file) && File.Exists(file) && FileCanBeRead(file))
{
MDM mdm = ServiceShared.HL7.Parser.GetMDM(file);
if (mdm != null)
{
ServiceShared.Models.Database.Results results = null;
Notification notification = null;
string pgs = "";
if (!string.IsNullOrEmpty(mdm.PGSInitial) && !string.IsNullOrEmpty(mdm.UDID))
{
pgs = mdm.PGSInitial;
results = dbResults.GetResults(mdm.PGSInitial, mdm.UDID);
}
else
{
pgs = mdm.PGS();
results = dbResults.GetResults(mdm.PGS(), mdm.UDID);
}
Log.Debug("[BW] PGS: " + pgs);
Log.Debug("[BW] UDID: " + mdm.UDID);
Log.Debug("[BW] ZIP: " + mdm.Zip);
Log.Debug("[BW] Birthday: " + mdm.Birthday);
Log.Debug("[BW] SampleId: " + mdm.SampleId);
Log.Debug("[BW] Status: " + mdm.ResultsStatus);
if (results != null)
{
Log.Debug("[BW] in db exists: ");
notification = new Notification();
notification.pgs = results.PGS;
notification.udid = results.UDID;
notification.created = DateTime.Now;
notification.status = mdm.ResultsStatus;
if (mdm.ResultsStatus == ServiceShared.Models.Database.Results.ResultsStatus.COMPLETED)
{
notification.available = true;
notification.available_ts = mdm.TimeStamp;
KeyPair keyPair = Curve25519.GenerateKeyPair();
byte[] deriveKey = keyPair.GetSharedKey(results.ClientPublicKey!);
if (deriveKey == null || deriveKey.Length <= 0 ||
!WriteEncryptedToPublic(mdm.PGS(), mdm.UDID, mdm.Base64Content, deriveKey) ||
!dbResults.UpdateStatus(results.PGS,
results.UDID,
true,
keyPair.PublicKey,
SHA512.Encrypt(mdm.Base64Content),
mdm.TimeStamp,
mdm.ResultsStatus,
mdm.PAT_HASH()))
{
results = null;
notification = null;
Log.Critical(new Exception("Could not move to the encrypted public storage" + file + ")"), "ServiceInside.Service.BackgroundWorker", "DoWorkAsync(object)");
}
}
else if(mdm.ResultsStatus == ServiceShared.Models.Database.Results.ResultsStatus.REJECTED)
{
Log.Debug("[BW] Status - Rejexted: " + file);
notification.available = false;
notification.available_ts = null;
dbResults.SetReject(results.PGS, results.UDID);
DeleteFilesFor(results.PGS, results.UDID);
}
else if (mdm.ResultsStatus == ServiceShared.Models.Database.Results.ResultsStatus.NOT_FOUND)
{
Log.Debug("[BW] Status - NOT_FOUND: " + file);
if((results.NotFoundCounter + 1) >= MaxTryNotFoundResults)
{
notification.available = false;
notification.available_ts = null;
}
else
{
notification = null;
}
dbResults.SetNotFound(results.PGS, results.UDID);
DeleteFilesFor(results.PGS, results.UDID);
}
else
{
CreateAck(results.PGS, results.PGS_HASH, AckTypes.ERROR, results.UDID, file, "MDM has no valid ResultsStatus file");
Log.Critical(new Exception("MDM has no valid ResultsStatus file: " + file), "ServiceInside.Service.BackgroundWorker", "DoWorkAsync");
}
}
else
{
// Get device by udid to get public key for encryption
ServiceShared.Models.Database.Device device = dbResults.GetDevice(mdm.UDID);
if (device != null && !string.IsNullOrEmpty(device.ClientPublicKey))
{
Log.Debug("[BW] device(" + mdm.UDID + ") in db found:");
KeyPair keyPair = Curve25519.GenerateKeyPair();
byte[] deriveKey = keyPair.GetSharedKey(device.ClientPublicKey!);
if (deriveKey != null && deriveKey.Length > 0 &&
WriteEncryptedToPublic(mdm.PGS(), mdm.UDID, mdm.Base64Content, deriveKey))
{
ServiceShared.Models.Database.Results newResults = new ServiceShared.Models.Database.Results();
newResults.PGS = mdm.PGS();
newResults.PGS_HASH = mdm.PGS_HASH(mdm.UDID);
newResults.Available = true;
newResults.AvailableTS = mdm.TimeStamp;
newResults.Status = mdm.ResultsStatus;
newResults.DeviceToken = device.DeviceToken;
newResults.DeviceType = device.DeviceType;
newResults.ClientPublicKey = device.ClientPublicKey;
newResults.UDID = device.UDID;
newResults.VerificationHash = device.VerificationHash;
newResults.ServerPublicKey = keyPair.PublicKey;
newResults.FileChecksum = SHA512.Encrypt(mdm.Base64Content);
if (dbResults.Create(newResults))
{
notification = new Notification();
notification.pgs = mdm.PGS();
notification.udid = mdm.UDID;
notification.available = true;
notification.available_ts = mdm.TimeStamp;
notification.status = mdm.ResultsStatus;
notification.created = DateTime.Now;
}
else
{
Log.Critical(new Exception("Could not create results in database MDM(" + file + ")"), "ServiceInside.Service.BackgroundWorker", "DoWorkAsync(object)");
}
}
}
else
{
Log.Debug("[BW] device " + mdm.UDID + " not found in db");
}
}
if (notification != null)
{
Log.Debug("[BW] notify");
ServiceOutside.Notify(notification);
}
Log.Trace("mdm(" + mdm.ResultsStatus + ")", pgs, mdm.UDID);
}
else
{
Log.Critical(new Exception("Not valid MDM(" + file + ")"), "ServiceInside.Service.BackgroundWorker", "DoWorkAsync(object)");
}
Log.Debug("[BW] delete file " + file);
// Delte original hl7 file
File.Delete(file);
}
else
{
Log.Debug("[BW] File: " + file + " not exists");
}
}
}
}
catch (Exception ex)
{
if(ex != null &&
!string.IsNullOrEmpty(ex.Message) &&
!ex.Message.Contains("process cannot access the file"))
{
Log.Critical(ex, "ServiceInside.Service.BackgroundWorker", "DoWorkAsync(object)");
}
if(!string.IsNullOrEmpty(file) && File.Exists(file))
{
File.Delete(file);
}
}
finally
{
if(!string.IsNullOrEmpty(file))
{
FreeWorker(workerThreadId);
}
}
}
/// <summary>
/// Deletes files for pgs and udid
/// </summary>
/// <param name="pgs">pgs that should be used in the new filename</param>
/// <param name="udid">udid that should be used in the new filename</param>
private static void DeleteFilesFor(string pgs, string udid)
{
try
{
if(!string.IsNullOrEmpty(pgs) && !string.IsNullOrEmpty(udid))
{
// Delete from public storage
string file = Path.Combine(_PublicDirectory, GetFileName(pgs, udid));
if(!string.IsNullOrEmpty(file) && File.Exists(file))
{
File.Delete(file);
}
// Delte from private storage
file = Path.Combine(_PrivateDirectory, GetFileName(pgs, udid, "hl7"));
if (!string.IsNullOrEmpty(file) && File.Exists(file))
{
File.Delete(file);
}
}
}
catch (Exception ex)
{
Log.Critical(ex, "ServiceInside.Service.BackgroundWorker", "DeleteFilesFor(string, string)");
}
}
/// <summary>
/// Encrypts and writes a content to the public directory
/// </summary>
/// <param name="pgs">pgs of results that will used as filename</param>
/// <param name="udid">udid of results that will used as filename</param>
/// <param name="content">content that should be encrypted</param>
/// <param name="deriveKey">shared derive key that should be used for the encrypton</param>
/// <returns></returns>
private static bool WriteEncryptedToPublic(string pgs, string udid, string content, byte[] deriveKey)
{
bool result = false;
try
{
if(!string.IsNullOrEmpty(pgs) && !string.IsNullOrEmpty(udid) && !string.IsNullOrEmpty(content) && deriveKey != null && deriveKey.Length > 0)
{
string encryptedFile = Path.Combine(_PublicDirectory, GetFileName(pgs, udid));
string encrypted = AES.Encrypt(content, deriveKey);
File.WriteAllText(encryptedFile, encrypted);
result = File.Exists(encryptedFile);
Log.Trace("encrypted move to (" + encryptedFile + ")", pgs, udid);
}
}
catch (Exception ex)
{
Log.Critical(ex, "ServiceInside.Service.BackgroundWorker", "WriteToPublic(string, string, string, byte[])");
}
return result;
}
/// <summary>
/// Removes worher Thread from the queue list
/// </summary>
/// <param name="threadId">Worker thread id</param>
private static void FreeWorker(string threadId)
{
try
{
if(threadId != null)
{
if (WorkerThreadIds.Contains(threadId))
{
WorkerThreadIds.Remove(threadId);
}
}
}
catch (Exception ex)
{
Log.Error(ex, "ServiceInside.Service.BackgroundWorker", "FreeWorker");
}
}
/// <summary>
/// Returns filename by pgs and udid SHA512 hash
/// </summary>
/// <param name="pgs">pgs hash that should be used in filename</param>
/// <param name="udid">udid that should be used in filename</param>
/// <param name="extension">file extension if should be set</param>
/// <param name="addTimestamp">flag if filename should be sufixed by timestamp</param>
/// <returns>returns SHA512 hash by (pgs + udid).extension</returns>
private static string GetFileName(string pgs, string udid, string extension = null, bool addTimestamp = false)
{
string result = null;
if(!string.IsNullOrEmpty(udid))
{
result = SHA512.Encrypt(udid + (!string.IsNullOrEmpty(pgs) ? pgs : "")) + (addTimestamp ? "_" + DateTime.Now.ToString("yyyyMMddHHmmss") : "") + (!string.IsNullOrEmpty(extension) ? "." + extension.ToLower() : "");
}
return result;
}
/// <summary>
/// Creates a ack log file for primary system
/// </summary>
/// <param name="udid">udid of mobile client</param>
/// <param name="pgs">pgs SHA512</param>
/// <param name="pgs_hash">pgs_hash AES Encrypted</param>
/// <param name="type">type of ack log file</param>
/// <param name="pat_hash">pat_hash AES Encrypted</param>
/// <returns></returns>
private static bool CreateAck(string udid, string pgs, string pgs_hash, AckTypes type, string pat_hash = null)
{
bool result = false;
try
{
string ack = "TYPE:" + type.ToString() + "\r\n";
ack += "UDID:" + udid + "\r\n";
if (!string.IsNullOrEmpty(pgs))
{
ack += "PGS:" + pgs + "\r\n";
if (!string.IsNullOrEmpty(pgs_hash))
{
string decrypted_values = AES.Decrypt(pgs_hash, AES.GetKey(udid + AES.PGS_ENCRYPT_PARTIAL_KEY));
if (!string.IsNullOrEmpty(decrypted_values))
{
string[] values = decrypted_values.Split('|');
if (values.Length == 3)
{
ack += "ZIP:" + values[0] + "\r\n";
ack += "BIRTHDATE:" + values[1] + "\r\n";
ack += "ORDER_ID:" + values[2] + "\r\n";
}
}
}
}
if(!string.IsNullOrEmpty(pat_hash))
{
string decrypted_pat_id = AES.Decrypt(pat_hash);
if(!string.IsNullOrEmpty(decrypted_pat_id))
{
ack += "PAT_ID:" + decrypted_pat_id + "\r\n";
}
}
ack += "CREATED:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
string ack_file = Path.Combine(_PrivateDirectoryAck, GetFileName(pgs, udid, "log", true));
if (File.Exists(ack_file))
{
File.Delete(ack_file);
}
File.WriteAllText(ack_file, ack);
result = File.Exists(ack_file);
}
catch (Exception ex)
{
Log.Critical(ex, "ServiceInside.Service.BackgroundWorker", "CreateAck(string, string, AckTypes");
}
return result;
}
/// <summary>
/// Creates a ack log file for primary system
/// </summary>
/// <param name="pgs">pgs for the error log file</param>
/// <param name="pgs_hash">pgs_hash that can be decrypted for plz, geb.datum, sampleid</param>
/// <param name="type">type of ack log file</param>
/// <param name="udid">udid for the error log file</param>
/// <param name="filename">filename, that contains error</param>
/// <param name="reason">reason of error</param>
private static bool CreateAck(string pgs, string pgs_hash, AckTypes type, string udid = null, string filename = null, string reason = null)
{
bool result = false;
try
{
string ack = "TYPE:" + type.ToString() + "\r\n";
ack += "UDID:" + udid + "\r\n";
if (!string.IsNullOrEmpty(pgs))
{
ack += "PGS:" + pgs + "\r\n";
if(!string.IsNullOrEmpty(pgs_hash))
{
string decrypted_values = AES.Decrypt(pgs_hash, AES.GetKey(udid + AES.PGS_ENCRYPT_PARTIAL_KEY));
if (!string.IsNullOrEmpty(decrypted_values))
{
string[] values = decrypted_values.Split('|');
if (values.Length == 3)
{
ack += "ZIP:" + values[0] + "\r\n";
ack += "BIRTHDATE:" + values[1] + "\r\n";
ack += "ORDER_ID:" + values[2] + "\r\n";
}
}
}
}
if (!string.IsNullOrEmpty(filename))
{
ack += "FILENAME:" + filename + "\r\n";
}
if (!string.IsNullOrEmpty(reason))
{
ack += "REASON:" + reason + "\r\n";
}
ack += "CREATED:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
string ack_file = Path.Combine(_PrivateDirectoryAck, GetFileName(pgs, udid, "log", true));
if (File.Exists(ack_file))
{
File.Delete(ack_file);
}
File.WriteAllText(ack_file, ack);
result = File.Exists(ack_file);
}
catch (Exception ex)
{
Log.Critical(ex, "ServiceInside.Service.BackgroundWorker", "CreateErrorLog(string, AckTypes, string, string, string)");
}
return result;
}
/// <summary>
/// Checks if file exists and it can be read
/// </summary>
/// <param name="file"></param>
/// <returns></returns>
private static bool FileCanBeRead(string file)
{
bool result = false;
try
{
if (!string.IsNullOrEmpty(file) && File.Exists(file))
{
using (var fs = new FileStream(file, FileMode.Open))
{
result = fs.CanRead;
}
}
}
catch
{
}
return result;
}
/// <summary>
/// Returns private directory path
/// </summary>
/// <returns></returns>
public static string GetPrivateDirectory()
{
return _PrivateDirectory;
}
/// <summary>
/// Returns private directory ack path
/// </summary>
/// <returns></returns>
public static string GetPrivateDirectoryAck()
{
return _PrivateDirectoryAck;
}
/// <summary>
/// Returns private directory ack done path
/// </summary>
/// <returns></returns>
public static string GetPrivateDirectoryAckDone()
{
return _PrivateDirectoryAckDone;
}
/// <summary>
/// Returns public directory path
/// </summary>
/// <returns></returns>
public static string GetPublicDirectory()
{
return _PublicDirectory;
}
}
}