using MySql.Data.MySqlClient; using ServiceShared.Models.Database; using static ServiceShared.Models.Database.Device; using static ServiceShared.Models.Database.Results; namespace ServiceShared.Database.Controllers { public class Results { private DbContext _DbContext; /// /// Constructor for results database controller /// /// Database context, that contains connection to the mysql database public Results(DbContext dbContext) { this._DbContext = dbContext; } /// /// Returns results by pgs and udid from the database /// /// pgs hash(partial pk) /// udid(partial pk) /// Returns results object from the database public Models.Database.Results GetResults(string pgs, string udid) { Models.Database.Results result = null; try { if(!string.IsNullOrEmpty(pgs) && !string.IsNullOrEmpty(udid)) { using (MySqlConnection client = this._DbContext.CreateConnection()) { client.Open(); MySqlCommand cmd = client.CreateCommand(); cmd.CommandText = @"select r.pgs, r.pgs_hash, d.udid, d.device_token, d.device_type, r.client_public_key, r.server_public_key, r.file_checksum, r.available, r.available_ts, r.picked_up, r.picked_up_ts, r.notified, r.notified_ts, d.verificator_hash, r.status, r.modified, r.created, r.not_found_counter, r.not_found_ts, r.notification_counter from results r inner join device d on d.udid = r.udid where r.pgs = @pgs and d.udid = @udid"; cmd.Parameters.Add(new MySqlParameter("pgs", pgs)); cmd.Parameters.Add(new MySqlParameter("udid", udid)); MySqlDataReader reader = cmd.ExecuteReader(); if (reader != null && reader.HasRows && reader.Read()) { result = this.ReadResults(reader); } client.Close(); Log.Write(cmd.CommandText + ", @pgs = " + pgs + ", @udid = " + udid, Log.Types.LOG); } } } catch (Exception ex) { Log.Critical(ex, "ServiceOutside.Database.Controllers.Results", "GetResults(string, string)"); } return result; } /// /// Returns not found results /// past minutes, after that the not found results should be checked again /// /// Returns results object from the database public List GetNotFoundResults(int pastMinutes, int maxTryNotFoundResults) { List result = null; try { using (MySqlConnection client = this._DbContext.CreateConnection()) { client.Open(); MySqlCommand cmd = client.CreateCommand(); cmd.CommandText = @"select r.pgs, r.pgs_hash, d.udid, d.device_token, d.device_type, r.client_public_key, r.server_public_key, r.file_checksum, r.available, r.available_ts, r.picked_up, r.picked_up_ts, r.notified, r.notified_ts, d.verificator_hash, r.status, r.modified, r.created, r.not_found_counter, r.not_found_ts, r.notification_counter from results r inner join device d on d.udid = r.udid where r.status = @status and r.not_found_counter < @max_not_found_counter and r.not_found_ts is not null"; cmd.Parameters.Add(new MySqlParameter("status", ServiceShared.Models.Database.Results.ResultsStatus.NOT_FOUND.ToString())); cmd.Parameters.Add(new MySqlParameter("max_not_found_counter", maxTryNotFoundResults)); MySqlDataReader reader = cmd.ExecuteReader(); if (reader != null && reader.HasRows) { result = new List(); DateTime now = DateTime.Now; while (reader.Read()) { Models.Database.Results results = this.ReadResults(reader); Log.Debug("[BW] check not found past minutes: " + (now - results.NotFoundTS.Value).TotalMinutes.ToString()); if (result != null && results.NotFoundTS.HasValue && (now - results.NotFoundTS.Value).TotalMinutes >= (pastMinutes - 5)) { result.Add(results); } } } client.Close(); } } catch (Exception ex) { Log.Critical(ex, "ServiceOutside.Database.Controllers.Results", "GetNotFoundResults(int)"); } return result; } /// /// Returns completed results that could not be notified /// /// Returns results object from the database public List GetNotNotifiedResults() { List result = null; try { using (MySqlConnection client = this._DbContext.CreateConnection()) { client.Open(); MySqlCommand cmd = client.CreateCommand(); cmd.CommandText = @"select r.pgs, r.pgs_hash, d.udid, d.device_token, d.device_type, r.client_public_key, r.server_public_key, r.file_checksum, r.available, r.available_ts, r.picked_up, r.picked_up_ts, r.notified, r.notified_ts, d.verificator_hash, r.status, r.modified, r.created, r.not_found_counter, r.not_found_ts, r.notification_counter from results r inner join device d on d.udid = r.udid where r.notified <> 1 and r.available = 1 and r.status = @status and r.notification_counter <= 3"; cmd.Parameters.Add(new MySqlParameter("status", ServiceShared.Models.Database.Results.ResultsStatus.COMPLETED.ToString())); MySqlDataReader reader = cmd.ExecuteReader(); if (reader != null && reader.HasRows) { result = new List(); while(reader.Read()) { Models.Database.Results results = this.ReadResults(reader); if(result != null) { result.Add(results); } } } client.Close(); } } catch (Exception ex) { Log.Critical(ex, "ServiceOutside.Database.Controllers.Results", "GetNotNotifiedResults()"); } return result; } /// /// Returns not found results that could not be notified /// /// Returns results object from the database public List GetNotFoundNotNotifiedResults(int maxNotFoundTries) { List result = null; try { using (MySqlConnection client = this._DbContext.CreateConnection()) { client.Open(); MySqlCommand cmd = client.CreateCommand(); cmd.CommandText = @"select r.pgs, r.pgs_hash, d.udid, d.device_token, d.device_type, r.client_public_key, r.server_public_key, r.file_checksum, r.available, r.available_ts, r.picked_up, r.picked_up_ts, r.notified, r.notified_ts, d.verificator_hash, r.status, r.modified, r.created, r.not_found_counter, r.not_found_ts, r.notification_counter from results r inner join device d on d.udid = r.udid where r.notified <> 1 and r.available = 0 and r.status = @status and r.not_found_counter >= @not_found_counter and r.notification_counter <= 3"; cmd.Parameters.Add(new MySqlParameter("status", ServiceShared.Models.Database.Results.ResultsStatus.NOT_FOUND.ToString())); cmd.Parameters.Add(new MySqlParameter("not_found_counter", maxNotFoundTries)); MySqlDataReader reader = cmd.ExecuteReader(); if (reader != null && reader.HasRows) { result = new List(); while (reader.Read()) { Models.Database.Results results = this.ReadResults(reader); if (result != null) { result.Add(results); } } } client.Close(); } } catch (Exception ex) { Log.Critical(ex, "ServiceOutside.Database.Controllers.Results", "GetNotFoundNotNotifiedResults()"); } return result; } /// /// Returns list of results by pgs(partial pk) from the database /// /// pgs hash(partial pk) /// Returns results object from the database public List GetResults(string pgs) { List result = null; try { if (!string.IsNullOrEmpty(pgs)) { using (MySqlConnection client = this._DbContext.CreateConnection()) { client.Open(); MySqlCommand cmd = client.CreateCommand(); cmd.CommandText = @"select r.pgs, r.pgs_hash, d.udid, d.device_token, d.device_type, r.client_public_key, r.server_public_key, r.file_checksum, r.available, r.available_ts, r.picked_up, r.picked_up_ts, r.notified, r.notified_ts, d.verificator_hash, r.status, r.modified, r.created, r.not_found_counter, r.not_found_ts, r.notification_counter from results r inner join device d on d.udid = r.udid where r.pgs = @pgs"; cmd.Parameters.Add(new MySqlParameter("pgs", pgs)); MySqlDataReader reader = cmd.ExecuteReader(); if (reader != null && reader.HasRows) { result = new List(); while(reader.Read()) { Models.Database.Results dbResults = this.ReadResults(reader); if(dbResults != null) { result.Add(dbResults); } } } client.Close(); Log.Write(cmd.CommandText + ", @pgs = " + pgs, Log.Types.LOG); } } } catch (Exception ex) { Log.Critical(ex, "ServiceOutside.Database.Controllers.Results", "GetResults(string)"); } return result; } /// /// Returns device by udid /// /// udid (pk) /// Returns Device object public Device GetDevice(string udid) { Device result = null; try { if (!string.IsNullOrEmpty(udid)) { using (MySqlConnection client = this._DbContext.CreateConnection()) { client.Open(); MySqlCommand cmd = client.CreateCommand(); cmd.CommandText = @"select d.udid, d.device_token, d.public_key, d.verificator_hash, d.pat_hash, d.pin, d.device_type, d.created, d.modified from device d where d.udid = @udid"; cmd.Parameters.Add(new MySqlParameter("udid", udid)); MySqlDataReader reader = cmd.ExecuteReader(); if (reader != null && reader.HasRows && reader.Read()) { result = this.ReadDevice(reader); } client.Close(); Log.Write(cmd.CommandText + ", @udid = " + udid, Log.Types.LOG); } } } catch (Exception ex) { Log.Critical(ex, "ServiceOutside.Database.Controllers.Results", "GetLastDeviceRecord(string)"); } return result; } /// /// Checks if device exists /// /// udid of device /// returns true if exists public bool DeviceExists(string udid) { bool result = false; try { if (!string.IsNullOrEmpty(udid)) { using (MySqlConnection client = this._DbContext.CreateConnection()) { client.Open(); MySqlCommand cmd = client.CreateCommand(); /** insert device **/ cmd.CommandText = @"select count(*) cnt from device where udid = @udid"; cmd.Parameters.Add(new MySqlParameter("udid", udid)); string res = cmd.ExecuteScalar().ToString(); if (!string.IsNullOrEmpty(res)) { int r = 0; if (Int32.TryParse(res, out r)) { result = (r > 0); } } client.Close(); } } } catch (Exception ex) { Log.Critical(ex, "ServiceOutside.Database.Controllers.Results", "DeviceExists(string)"); } return result; } /// /// Creates results in the database /// /// Results object /// returns true if success public bool Create(Models.Database.Results results) { bool result = false; try { if (results != null && !string.IsNullOrEmpty(results.PGS) && results.PGS.Length == 128 && !string.IsNullOrEmpty(results.PGS_HASH) && !string.IsNullOrEmpty(results.UDID) && (results.UDID.Length >= 10 && results.UDID.Length <= 64) && !string.IsNullOrEmpty(results.DeviceToken) && (results.DeviceToken.Length >= 10 && results.DeviceToken.Length <= 255) && results.DeviceType != DeviceTypes.NONE && !string.IsNullOrEmpty(results.ClientPublicKey) && (results.ClientPublicKey.Length >= 32 && results.ClientPublicKey.Length <= 64) && !string.IsNullOrEmpty(results.VerificationHash) && results.VerificationHash.Length == 128) { using (MySqlConnection client = this._DbContext.CreateConnection()) { client.Open(); MySqlCommand cmd = client.CreateCommand(); if(!this.DeviceExists(results.UDID) && !string.IsNullOrEmpty(results.PIN)) { /** insert device **/ cmd.CommandText = @"insert into device (udid, device_token, public_key, verificator_hash, pin, device_type) values (@udid, @device_token, @public_key, @verificator_hash, @pin, @device_type)"; cmd.Parameters.Add(new MySqlParameter("udid", results.UDID)); cmd.Parameters.Add(new MySqlParameter("device_token", results.DeviceToken)); cmd.Parameters.Add(new MySqlParameter("public_key", results.ClientPublicKey)); cmd.Parameters.Add(new MySqlParameter("verificator_hash", results.VerificationHash)); cmd.Parameters.Add(new MySqlParameter("pin", results.PIN)); cmd.Parameters.Add(new MySqlParameter("device_type", results.DeviceType)); cmd.ExecuteNonQuery(); /** log **/ Log.Write(cmd.CommandText + "@udid = " + results.UDID + ", @device_token = " + results.DeviceToken + ", @public_key = " + results.ClientPublicKey + ", @verificator_hash = " + results.VerificationHash.ToString() + ", @pin = " + results.PIN + ", @device_type = " + results.DeviceType, Log.Types.LOG); } /** insert results **/ cmd = client.CreateCommand(); cmd.CommandText = @"insert into results (pgs, pgs_hash, udid, client_public_key, server_public_key, available, available_ts, status, file_checksum) values (@pgs, @pgs_hash, @udid, @client_public_key, @server_public_key, @available, @available_ts, @status, @file_checksum)"; cmd.Parameters.Add(new MySqlParameter("pgs", results.PGS)); cmd.Parameters.Add(new MySqlParameter("pgs_hash", results.PGS_HASH)); cmd.Parameters.Add(new MySqlParameter("udid", results.UDID)); cmd.Parameters.Add(new MySqlParameter("client_public_key", results.ClientPublicKey)); cmd.Parameters.Add(new MySqlParameter("server_public_key", (!string.IsNullOrEmpty(results.ServerPublicKey) ? results.ServerPublicKey : DBNull.Value))); cmd.Parameters.Add(new MySqlParameter("available", results.Available)); cmd.Parameters.Add(new MySqlParameter("available_ts", (results.AvailableTS.HasValue ? results.AvailableTS.Value : DBNull.Value))); cmd.Parameters.Add(new MySqlParameter("status", results.Status.ToString())); cmd.Parameters.Add(new MySqlParameter("file_checksum", (!string.IsNullOrEmpty(results.FileChecksum) ? results.FileChecksum : DBNull.Value))); cmd.ExecuteNonQuery(); result = true; client.Close(); Log.Write(cmd.CommandText + ", @pgs = " + results.PGS + ", @pgs_hash = " + results.PGS_HASH + ", @udid = " + results.UDID + ", @client_public_key = " + results.ClientPublicKey + ", @server_public_key = " + (!string.IsNullOrEmpty(results.ServerPublicKey) ? results.ServerPublicKey : "") + ", @available = " + results.Available.ToString() + ", @available_ts = " + (results.AvailableTS.HasValue ? results.AvailableTS.Value.ToString() : "") + ", @status = " + results.Status.ToString() + ", @file_checksum = " + (!string.IsNullOrEmpty(results.FileChecksum) ? results.FileChecksum : ""), Log.Types.LOG); } } else { Log.Critical(new Exception("Missing some of parameters to insert into results"), "ServiceOutside.Database.Controllers.Results", "Create(Models.Results)"); } } catch (Exception ex) { Log.Critical(ex, "ServiceOutside.Database.Controllers.Results", "Create(Models.Results)"); } return result; } /// /// Updates status of results in the database /// /// pgs (partial pk) /// udid (partial pk) /// available, that should be set /// server public key, that was used for the encryption by server side /// Checksum of file content(base64) before encrypted /// timestamp of available, that should be set /// status of results /// patient aes hash that can be decrypted by service inside /// Returns true if success public bool UpdateStatus(string pgs, string udid, bool available, string server_public_key, string file_checksum, DateTime available_ts, ResultsStatus status, string pat_hash = null) { bool result = false; try { if (!string.IsNullOrEmpty(pgs) && pgs.Length == 128 && !string.IsNullOrEmpty(udid) && status != ResultsStatus.NONE) { using (MySqlConnection client = this._DbContext.CreateConnection()) { client.Open(); MySqlCommand cmd = client.CreateCommand(); cmd.CommandText = @"update results r inner join device d on d.udid = r.udid set r.available = @available, r.available_ts = @available_ts, r.status = @status, r.server_public_key = @server_public_key, r.file_checksum = @file_checksum, r.picked_up = 0, r.picked_up_ts = null, r.modified = now(), d.modified = now(), d.pat_hash = @pat_hash where r.pgs = @pgs and d.udid = @udid"; cmd.Parameters.Add(new MySqlParameter("available", available)); cmd.Parameters.Add(new MySqlParameter("available_ts", available_ts.ToString("yyyy-MM-dd HH:mm:ss"))); cmd.Parameters.Add(new MySqlParameter("status", status.ToString())); cmd.Parameters.Add(new MySqlParameter("server_public_key", (!string.IsNullOrEmpty(server_public_key) ? server_public_key : DBNull.Value))); cmd.Parameters.Add(new MySqlParameter("file_checksum", (!string.IsNullOrEmpty(file_checksum) ? file_checksum : DBNull.Value))); cmd.Parameters.Add(new MySqlParameter("pat_hash", (!string.IsNullOrEmpty(pat_hash) ? pat_hash : DBNull.Value))); cmd.Parameters.Add(new MySqlParameter("pgs", pgs)); cmd.Parameters.Add(new MySqlParameter("udid", udid)); cmd.ExecuteNonQuery(); result = true; client.Close(); Log.Write(cmd.CommandText + ", @available = " + available + ", @available_ts = " + available_ts.ToString("yyyy-MM-dd HH:mm:ss") + ", @status = " + status.ToString() + ", @server_public_key = " + (!string.IsNullOrEmpty(server_public_key) ? server_public_key : "" ) + ", @file_checksum = " + (!string.IsNullOrEmpty(file_checksum) ? file_checksum : "") + ", @pat_hash = " + (!string.IsNullOrEmpty(pat_hash) ? pat_hash : "") + ", @pgs = " + pgs + ", @udid = " + udid, Log.Types.LOG); } } } catch (Exception ex) { Log.Critical(ex, "ServiceOutside.Database.Controllers.Results", "UpdateStatus(string, string, bool, string, string, DateTime, ResultsStatus, string=null)"); } return result; } /// /// Updates status to rejected /// /// pgs(partial pk), that should be updated /// udid(partial pk), that should be updated /// returns true if success public bool SetReject(string pgs, string udid) { bool result = false; try { if (!string.IsNullOrEmpty(pgs) && pgs.Length == 128 && !string.IsNullOrEmpty(udid)) { using (MySqlConnection client = this._DbContext.CreateConnection()) { client.Open(); MySqlCommand cmd = client.CreateCommand(); cmd.CommandText = @"update results set status = @status, available = 0, available_ts = null, file_checksum = null, modified = now() where pgs = @pgs and udid = @udid"; cmd.Parameters.Add(new MySqlParameter("status", ResultsStatus.REJECTED.ToString())); cmd.Parameters.Add(new MySqlParameter("pgs", pgs)); cmd.Parameters.Add(new MySqlParameter("udid", udid)); cmd.ExecuteNonQuery(); result = true; client.Close(); Log.Write(cmd.CommandText + ", @status = " + ResultsStatus.REJECTED.ToString() + ", @pgs = " + pgs + ", @udid = " + udid, Log.Types.LOG); } } } catch (Exception ex) { Log.Critical(ex, "ServiceOutside.Database.Controllers.Results", "SetReject(string, string)"); } return result; } /// /// Updates status to NOT_FOUND /// /// pgs(partial pk), that should be updated /// udid(partial pk), that should be updated /// returns true if success public bool SetNotFound(string pgs, string udid) { bool result = false; try { if (!string.IsNullOrEmpty(pgs) && pgs.Length == 128 && !string.IsNullOrEmpty(udid)) { using (MySqlConnection client = this._DbContext.CreateConnection()) { client.Open(); MySqlCommand cmd = client.CreateCommand(); cmd.CommandText = @"update results set status = @status, available = 0, available_ts = null, file_checksum = null, not_found_counter = not_found_counter + 1, not_found_ts = now(), modified = now() where pgs = @pgs and udid = @udid"; cmd.Parameters.Add(new MySqlParameter("status", ResultsStatus.NOT_FOUND.ToString())); cmd.Parameters.Add(new MySqlParameter("pgs", pgs)); cmd.Parameters.Add(new MySqlParameter("udid", udid)); cmd.ExecuteNonQuery(); result = true; client.Close(); Log.Write(cmd.CommandText + ", @status = " + ResultsStatus.NOT_FOUND.ToString() + ", @pgs = " + pgs + ", @udid = " + udid, Log.Types.LOG); } } } catch (Exception ex) { Log.Critical(ex, "ServiceOutside.Database.Controllers.Results", "SetNotFound(string, string)"); } return result; } /// /// Sets the notified flag to true and timestamp of notified flag /// /// pgs(partial pk), that should be updated /// udid(partial pk), that should be updated /// returns true if success public bool SetNotified(string pgs, string udid) { bool result = false; try { if (!string.IsNullOrEmpty(pgs) && pgs.Length == 128) { using (MySqlConnection client = this._DbContext.CreateConnection()) { client.Open(); MySqlCommand cmd = client.CreateCommand(); cmd.CommandText = @"update results set notified = 1, notified_ts = now(), notification_counter = notification_counter + 1, modified = now() where pgs = @pgs and udid = @udid"; cmd.Parameters.Add(new MySqlParameter("pgs", pgs)); cmd.Parameters.Add(new MySqlParameter("udid", udid)); cmd.ExecuteNonQuery(); result = true; client.Close(); Log.Write(cmd.CommandText + ", @pgs = " + pgs + ", @udid = " + udid, Log.Types.LOG); } } } catch (Exception ex) { Log.Critical(ex, "ServiceOutside.Database.Controllers.Results", "SetNotified(string, string)"); } return result; } /// /// Sets counter of notification try /// /// pgs(partial pk), that should be updated /// udid(partial pk), that should be updated /// returns true if success public bool SetNotificationTry(string pgs, string udid) { bool result = false; try { if (!string.IsNullOrEmpty(pgs) && pgs.Length == 128) { using (MySqlConnection client = this._DbContext.CreateConnection()) { client.Open(); MySqlCommand cmd = client.CreateCommand(); cmd.CommandText = @"update results set notification_counter = notification_counter + 1, modified = now() where pgs = @pgs and udid = @udid"; cmd.Parameters.Add(new MySqlParameter("pgs", pgs)); cmd.Parameters.Add(new MySqlParameter("udid", udid)); cmd.ExecuteNonQuery(); result = true; client.Close(); Log.Write(cmd.CommandText + ", @pgs = " + pgs + ", @udid = " + udid, Log.Types.LOG); } } } catch (Exception ex) { Log.Critical(ex, "ServiceOutside.Database.Controllers.Results", "SetNotificationTry(string, string)"); } return result; } /// /// Sets the pickedup flag to true and timestamp of pickedup flag /// /// pgs(partial pk), that should be updated /// udid(partial pk), that should be updated /// returns true if success public bool SetPickedUp(string pgs, string udid) { bool result = false; try { if (!string.IsNullOrEmpty(pgs) && pgs.Length == 128) { using (MySqlConnection client = this._DbContext.CreateConnection()) { client.Open(); MySqlCommand cmd = client.CreateCommand(); cmd.CommandText = @"update results set picked_up = 1, picked_up_ts = now(), modified = now() where pgs = @pgs and udid = @udid"; cmd.Parameters.Add(new MySqlParameter("pgs", pgs)); cmd.Parameters.Add(new MySqlParameter("udid", udid)); cmd.ExecuteNonQuery(); result = true; client.Close(); Log.Write(cmd.CommandText + ", @pgs = " + pgs + ", @udid = " + udid, Log.Types.LOG); } } } catch (Exception ex) { Log.Critical(ex, "ServiceOutside.Database.Controllers.Results", "SetPickedUp(string, string)"); } return result; } /// /// Delete results in the database /// /// pgs hash(partial pk) /// udid hash(partial pk) /// returns true if success public bool Delete(string pgs, string udid) { bool result = false; try { if (!string.IsNullOrEmpty(pgs) && pgs.Length == 128 && !string.IsNullOrEmpty(udid)) { using (MySqlConnection client = this._DbContext.CreateConnection()) { client.Open(); MySqlCommand cmd = client.CreateCommand(); cmd.CommandText = @"delete from results where pgs = @pgs and udid = @udid"; cmd.Parameters.Add(new MySqlParameter("pgs", pgs)); cmd.Parameters.Add(new MySqlParameter("udid", udid)); cmd.ExecuteNonQuery(); result = true; client.Close(); Log.Write(cmd.CommandText + ", @pgs = " + pgs + ", @udid = " + udid, Log.Types.LOG); } } } catch (Exception ex) { Log.Critical(ex, "ServiceOutside.Database.Controllers.Results", "Delete(string, string)"); } return result; } /// /// Delete statuses from the database /// /// Statuses must contain pgs and udid /// /// returns true if success public bool RemoveStatuses(List statuses) { bool result = false; try { if (statuses != null && statuses.Count > 0) { foreach(ServiceShared.Models.Database.Status status in statuses) { result = this.Delete(status.pgs, status.udid); } } } catch (Exception ex) { Log.Critical(ex, "ServiceOutside.Database.Controllers.Results", "RemoveStatuses(List)"); } return result; } /// /// Removes all recors for the device /// /// udid hash(partial pk) /// verificator_hash, that validate the client permission /// returns true if success public bool DeleteDevice(string udid, string verificator_hash) { bool result = false; try { if (!string.IsNullOrEmpty(udid) && !string.IsNullOrEmpty(udid)) { using (MySqlConnection client = this._DbContext.CreateConnection()) { client.Open(); MySqlCommand cmd = client.CreateCommand(); cmd.CommandText = @"delete from device where udid = @udid and verificator_hash = @verificator_hash"; cmd.Parameters.Add(new MySqlParameter("udid", udid)); cmd.Parameters.Add(new MySqlParameter("verificator_hash", verificator_hash)); cmd.ExecuteNonQuery(); result = true; client.Close(); Log.Write(cmd.CommandText + ", @udid = " + udid + ", @verificator_hash = " + verificator_hash, Log.Types.LOG); /** Results will be removed by cascade action **/ } } } catch (Exception ex) { Log.Critical(ex, "ServiceOutside.Database.Controllers.Results", "Delete(string, string)"); } return result; } /// /// Updates verificator hash for the device /// /// udid hash(partial pk) /// client pin /// old verificator hash /// new verificator hash /// returns true if success public bool UpdateVerificatorHash(string udid, string pin, string old_verificator_hash, string new_verificator_hash) { bool result = false; try { if (!string.IsNullOrEmpty(udid) && !string.IsNullOrEmpty(old_verificator_hash) && old_verificator_hash.Length == 128 && !string.IsNullOrEmpty(new_verificator_hash) && new_verificator_hash.Length == 128 && !string.IsNullOrEmpty(pin) && pin.Length == 5) { using (MySqlConnection client = this._DbContext.CreateConnection()) { client.Open(); MySqlCommand cmd = client.CreateCommand(); cmd.CommandText = @"update device set verificator_hash = @new_verificator_hash, pin = @pin where udid = @udid and verificator_hash = @old_verificator_hash"; cmd.Parameters.Add(new MySqlParameter("new_verificator_hash", new_verificator_hash)); cmd.Parameters.Add(new MySqlParameter("pin", pin)); cmd.Parameters.Add(new MySqlParameter("udid", udid)); cmd.Parameters.Add(new MySqlParameter("old_verificator_hash", old_verificator_hash)); cmd.ExecuteNonQuery(); result = true; client.Close(); Log.Write(cmd.CommandText + ", @new_verificator_hash = " + new_verificator_hash + ", @new_verificator_hash = " + old_verificator_hash + ", @udid = " + udid, Log.Types.LOG); } } } catch (Exception ex) { Log.Critical(ex, "ServiceOutside.Database.Controllers.Results", "UpdateVerificatorHash(string, string, string)"); } return result; } /// /// Updates device token for client /// /// udid hash(partial pk) /// client verificator_hash /// device_token that should be updated /// returns true if success public bool UpdateDeviceToken(string udid, string verificator_hash, string device_token) { bool result = false; try { if (!string.IsNullOrEmpty(udid) && !string.IsNullOrEmpty(verificator_hash) && verificator_hash.Length == 128 && !string.IsNullOrEmpty(device_token)) { using (MySqlConnection client = this._DbContext.CreateConnection()) { client.Open(); MySqlCommand cmd = client.CreateCommand(); cmd.CommandText = @"update device set device_token = @device_token where udid = @udid and verificator_hash = @verificator_hash"; cmd.Parameters.Add(new MySqlParameter("device_token", device_token)); cmd.Parameters.Add(new MySqlParameter("udid", udid)); cmd.Parameters.Add(new MySqlParameter("verificator_hash", verificator_hash)); cmd.ExecuteNonQuery(); result = true; client.Close(); Log.Write(cmd.CommandText + ", @device_token = " + device_token + ", @udid = " + udid + ", @verificator_hash = " + verificator_hash, Log.Types.LOG); } } } catch (Exception ex) { Log.Critical(ex, "ServiceOutside.Database.Controllers.Results", "UpdateDeviceToken(string, string, string)"); } return result; } /// /// Check if device with verificator_hash has a record in the database /// // udid hash(partial pk) /// verificator_hash, that validate the client permission /// returns true if some of records could be found else false public bool DeviceHasResults(string udid, string verificator_hash) { bool result = false; try { using (MySqlConnection client = this._DbContext.CreateConnection()) { client.Open(); MySqlCommand cmd = client.CreateCommand(); cmd.CommandText = @"select count(*) cnt from results r inner join device d on d.udid = r.udid where r.udid = @udid and d.verificator_hash = @verificator_hash"; cmd.Parameters.Add(new MySqlParameter("udid", udid)); cmd.Parameters.Add(new MySqlParameter("verificator_hash", verificator_hash)); cmd.ExecuteNonQuery(); string res = cmd.ExecuteScalar().ToString(); if (!string.IsNullOrEmpty(res)) { int r = 0; if (Int32.TryParse(res, out r)) { result = (r > 0); } } client.Close(); Log.Write(cmd.CommandText + ", @udid = " + udid + ", @verificator_hash = " + verificator_hash, Log.Types.LOG); } } catch (Exception ex) { Log.Error(ex, "ServiceOutside.Database.Controllers.Results", "DeviceHasResults(string, string)"); } return result; } /// /// Returns results by udid /// /// udid hash(partial pk) /// verificator_hash, that validate the client permission /// returns true if some of records could be found else false public List GetDeviceResults(string udid, string verificator_hash) { List result = null; try { using (MySqlConnection client = this._DbContext.CreateConnection()) { client.Open(); MySqlCommand cmd = client.CreateCommand(); cmd.CommandText = @"select r.pgs, r.pgs_hash, d.udid, d.device_token, d.device_type, r.client_public_key, r.server_public_key, r.file_checksum, r.available, r.available_ts, r.picked_up, r.picked_up_ts, r.notified, r.notified_ts, d.verificator_hash, d.pin, r.status, r.modified, r.created, r.not_found_counter, r.not_found_ts, r.notification_counter from device d inner join results r on r.udid = d.udid where d.udid = @udid and d.verificator_hash = @verificator_hash"; cmd.Parameters.Add(new MySqlParameter("udid", udid)); cmd.Parameters.Add(new MySqlParameter("verificator_hash", verificator_hash)); MySqlDataReader reader = cmd.ExecuteReader(); if (reader != null && reader.HasRows) { result = new List(); while (reader.Read()) { Models.Database.Results dbResults = this.ReadResults(reader); if (dbResults != null) { result.Add(dbResults); } } } client.Close(); Log.Write(cmd.CommandText + ", @udid = " + udid + ", @verificator_hash = " + verificator_hash, Log.Types.LOG); } } catch (Exception ex) { Log.Error(ex, "ServiceOutside.Database.Controllers.Results", "GetDeviceResults(string, string)"); } return result; } /// /// Deletes not mapped results for x old days from the database /// /// number of old days /// true if success public bool CleanUp(int oldDays) { bool result = false; try { if (oldDays > 1) { using (MySqlConnection client = this._DbContext.CreateConnection()) { client.Open(); MySqlCommand cmd = client.CreateCommand(); cmd.CommandText = "delete from results where datediff(now(), created) > @olddays"; cmd.Parameters.Add(new MySqlParameter("olddays", oldDays)); cmd.ExecuteNonQuery(); result = true; client.Close(); Log.Write(cmd.CommandText + ", @olddays = " + oldDays, Log.Types.LOG); } } } catch (Exception ex) { Log.Error(ex, "ServiceOutside.Database.Controllers.Results", "CleanUpNotMapped(int)"); } return result; } /// /// Count opened reults(not pickedup) for the udid /// /// Results with UDID and IMEI /// public int CountOpenedRequest(Models.Database.Results results) { int result = 0; try { using (MySqlConnection client = this._DbContext.CreateConnection()) { client.Open(); MySqlCommand cmd = client.CreateCommand(); cmd.CommandText = @"select count(*) cnt from device d inner join results r on r.udid = d.udid where d.udid = @udid and d.verificator_hash = @verificator_hash and r.picked_up <> 1"; cmd.Parameters.Add(new MySqlParameter("udid", results.UDID)); cmd.Parameters.Add(new MySqlParameter("verificator_hash", results.VerificationHash)); cmd.ExecuteNonQuery(); string res = cmd.ExecuteScalar().ToString(); if(!string.IsNullOrEmpty(res)) { int r = 0; if(Int32.TryParse(res, out r)) { result = r; } } client.Close(); Log.Write(cmd.CommandText + "@udid = " + results.UDID + "@verificator_hash = " + results.VerificationHash, Log.Types.LOG); } } catch (Exception ex) { Log.Error(ex, "ServiceOutside.Database.Controllers.Results", "CountOpenedRequest(Models.Results)"); } return result; } /// /// Returns all of opened(still not pickedup) pgs´s requests for the device /// /// public List GetOpened(string udid, string verificator_hash, int maxOpenedRequests = 10, int maxNotFoundTries = 2) { List result = null; try { using (MySqlConnection client = this._DbContext.CreateConnection()) { client.Open(); MySqlCommand cmd = client.CreateCommand(); cmd.CommandText = @"select r.pgs, r.pgs_hash, d.udid, d.device_token, d.device_type, r.client_public_key, r.server_public_key, r.file_checksum, r.available, r.available_ts, r.picked_up, r.picked_up_ts, r.notified, r.notified_ts, d.verificator_hash, r.status, r.modified, r.created, r.not_found_counter, r.not_found_ts, r.notification_counter from device d inner join results r on r.udid = d.udid where d.udid = @udid and d.verificator_hash = @verificator_hash and r.picked_up <> 1 limit " + maxOpenedRequests; cmd.Parameters.Add(new MySqlParameter("udid", udid)); cmd.Parameters.Add(new MySqlParameter("verificator_hash", verificator_hash)); MySqlDataReader reader = cmd.ExecuteReader(); if (reader != null && reader.HasRows) { if(result == null) { result = new List(); } while(reader.Read()) { Models.Database.Results results = this.ReadResults(reader); if(results != null) { if(results.Status == ResultsStatus.NOT_FOUND) { Log.Debug("Found not found results try " + results.NotFoundCounter + "/" + maxNotFoundTries); /** send only not found results if max tries has been reached **/ if (results.NotFoundCounter >= maxNotFoundTries) { result.Add(new Status(results)); } } else { result.Add(new Status(results)); } } } } client.Close(); Log.Write(cmd.CommandText + ", @udid = " + udid + ", @verificator_hash = " + verificator_hash, Log.Types.LOG); } } catch (Exception ex) { Log.Error(ex, "ServiceOutside.Database.Controllers.Results", "GetOpened(string, string, int)"); } return result; } /// /// Returns pin of device /// /// returns pin of device public string GetPIN(string udid, string verificator_hash) { string result = null; try { using (MySqlConnection client = this._DbContext.CreateConnection()) { client.Open(); MySqlCommand cmd = client.CreateCommand(); cmd.CommandText = @"select d.pin from device d where d.udid = @udid and d.verificator_hash = @verificator_hash"; cmd.Parameters.Add(new MySqlParameter("udid", udid)); cmd.Parameters.Add(new MySqlParameter("verificator_hash", verificator_hash)); MySqlDataReader reader = cmd.ExecuteReader(); if (reader != null && reader.HasRows) { while (reader.Read()) { result = (string)reader["pin"]; } } client.Close(); Log.Write(cmd.CommandText + ", @udid = " + udid + ", @verificator_hash = " + verificator_hash, Log.Types.LOG); } } catch (Exception ex) { Log.Error(ex, "ServiceOutside.Database.Controllers.Results", "GetPIN(string, string)"); } return result; } /// /// Reads results from mysql data reader /// /// MySqlDataReader with current connection context /// Returns Results object private Models.Database.Results ReadResults(MySqlDataReader reader) { Models.Database.Results result = null; try { if (reader["pgs"] != null && reader["pgs"] != DBNull.Value) { result = new Models.Database.Results(); result.PGS = (string)reader["pgs"]; result.PGS_HASH = (string)reader["pgs_hash"]; result.UDID = (string)reader["udid"]; result.DeviceToken = (string)reader["device_token"]; result.ClientPublicKey = (string)reader["client_public_key"]; result.ServerPublicKey = (reader["server_public_key"] != null && reader["server_public_key"] != DBNull.Value) ? (string)reader["server_public_key"] : null; result.FileChecksum = (reader["file_checksum"] != null && reader["file_checksum"] != DBNull.Value) ? (string)reader["file_checksum"] : null; result.Available = (reader["available"] != null && reader["available"] != DBNull.Value && Convert.ToInt32(reader["available"].ToString()) == 1); result.AvailableTS = ((reader["available_ts"] != null && reader["available_ts"] != DBNull.Value) ? (DateTime)reader["available_ts"] : null); result.PickedUp = (reader["picked_up"] != null && reader["picked_up"] != DBNull.Value && Convert.ToInt32(reader["picked_up"].ToString()) == 1); result.PickedUpTS = (reader["picked_up_ts"] != null && reader["picked_up_ts"] != DBNull.Value) ? (DateTime)reader["picked_up_ts"] : null; result.Notified = (reader["notified"] != null && reader["notified"] != DBNull.Value && Convert.ToInt32(reader["notified"].ToString()) == 1); result.NotifiedTS = (reader["notified_ts"] != null && reader["notified_ts"] != DBNull.Value) ? (DateTime)reader["notified_ts"] : null; result.VerificationHash = (string)reader["verificator_hash"]; result.Modified = (reader["modified"] != null && reader["modified"] != DBNull.Value) ? (DateTime)reader["modified"] : null; result.Created = (DateTime)reader["created"]; string device_type = ((reader["device_type"] != null && reader["device_type"] != DBNull.Value) ? (string)reader["device_type"] : null); DeviceTypes deviceType = DeviceTypes.NONE; if (!string.IsNullOrEmpty(device_type) && Enum.TryParse(device_type, out deviceType) && deviceType != DeviceTypes.NONE) { result.DeviceType = deviceType; } string status = ((reader["status"] != null && reader["status"] != DBNull.Value) ? (string)reader["status"] : null); ResultsStatus outStatus = ResultsStatus.NONE; if (!string.IsNullOrEmpty(status) && Enum.TryParse(status, out outStatus) && outStatus != ResultsStatus.NONE) { result.Status = outStatus; } result.NotFoundCounter = (reader["not_found_counter"] != null && reader["not_found_counter"] != DBNull.Value ?Convert.ToInt32(reader["not_found_counter"].ToString()) : 0); result.NotFoundTS = (reader["not_found_ts"] != null && reader["not_found_ts"] != DBNull.Value) ? (DateTime)reader["not_found_ts"] : null; result.NotificationCounter = (reader["notification_counter"] != null && reader["notification_counter"] != DBNull.Value ? Convert.ToInt32(reader["notification_counter"].ToString()) : 0); } } catch (Exception ex) { Log.Critical(ex, "ServiceOutside.Database.Controllers.Results", "ReadResults(MySqlDataReader)"); } return result; } /// /// Reads device from mysql data reader /// /// MySqlDataReader with current connection context /// Returns device object private Device ReadDevice(MySqlDataReader reader) { Device result = null; try { if (reader["udid"] != null && reader["udid"] != DBNull.Value) { result = new Device(); result.UDID = (string)reader["udid"]; result.DeviceToken = (string)reader["device_token"]; result.VerificationHash = (string)reader["verificator_hash"]; result.ClientPublicKey = (string)reader["public_key"]; result.PIN = (string)reader["pin"]; result.PatHash = ((reader["pat_hash"] != null && reader["pat_hash"] != DBNull.Value) ? (string)reader["pat_hash"] : null); string device_type = ((reader["device_type"] != null && reader["device_type"] != DBNull.Value) ? (string)reader["device_type"] : null); Device.DeviceTypes deviceType = Device.DeviceTypes.NONE; if (!string.IsNullOrEmpty(device_type) && Enum.TryParse(device_type, out deviceType) && deviceType != Device.DeviceTypes.NONE) { result.DeviceType = deviceType; } result.Modified = (reader["modified"] != null && reader["modified"] != DBNull.Value) ? (DateTime)reader["modified"] : null; result.Created = (DateTime)reader["created"]; } } catch (Exception ex) { Log.Critical(ex, "ServiceOutside.Database.Controllers.Results", "ReadDevice(MySqlDataReader)"); } return result; } } }