patbef-ServiceInside/ServiceInside/Service/BackgroundWorker.cs

1046 lines
44 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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;
}
}
}