using DeviceId; using EgwControlCenter.Core.DTO; using EgwControlCenter.Core.Models; using EgwCoreLib.Utils; using Newtonsoft.Json; using NLog; using System.Diagnostics; using System.IO.Compression; using System.Reflection; using System.Threading.Tasks; namespace EgwControlCenter.Core { public class AppControlService : IAppControlService { #region Public Fields /// /// Dizionario completo info macchina /// public static Dictionary DictMachineInfo = new Dictionary(); /// /// Dizionario completo info networking /// public static Dictionary DictNetInfo = new Dictionary(); /// /// Dizionario completo info utente /// public static Dictionary DictUserInfo = new Dictionary(); #endregion Public Fields #region Public Constructors public AppControlService() { try { mainAssembly = Assembly.GetCallingAssembly(); string startDir = Path.GetDirectoryName(mainAssembly.Location)!; ConfDir = startDir; CodImpiego = SLicManager.CodImpiego(); // setup RuntimConf (da gestire anche con set remoto...) RuntimeConfDict.Add("CountNumFastCheck", "60"); // chiedo info x fare setup DeviceName... var allInfo = DeviceInfoDict(); DeviceName = DictUserInfo != null && DictUserInfo.ContainsKey("MachineName") ? DictUserInfo["MachineName"] : "UnkDevice"; //DataDir = Environment.GetEnvironmentVariable("ClickOnce_DataDirectory") ?? startDir; string appData = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData); if (string.IsNullOrEmpty(appData)) { appData = "C:\\ProgramData"; } DataDir = Path.Combine(appData, "EgalWare", "AppControlCenter"); Log.Trace($"appData: {appData} | EnvData: {Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData)}"); // verifico esistenza directory... Directory.CreateDirectory(DataDir); CurrCheck = new ReleaseChecker(ConfDir, DataDir); Log.Trace($"Folder Setup | {ConfDir} | {DataDir}"); } catch (Exception exc) { Log.Error($"Error in AppControlService.init:{Environment.NewLine}{exc}"); } } #endregion Public Constructors #region Public Events /// /// Evento update configurazione /// public event Action EA_ConfigUpdated = null!; /// /// Evento richiesta reload applicazione /// public event Action EA_ReloadRequested = null!; /// /// Evento calling a remote in corso /// public event Action EA_RemoteCalling = null!; /// /// Evento richiesta update applicazione (con check update app) /// public event Action EA_RestartRequested = null!; /// /// Evento update status controlli /// public event Action EA_StatusUpdated = null!; #endregion Public Events #region Public Properties public string AppKey { get => CurrCheck.CurrPatrolCont.AppKey; set { if (CurrCheck.CurrPatrolCont.AppKey != value) { // verifico ammissibilità CurrCheck.CurrPatrolCont.AppKey = value; ReportConfigUpd(); } } } /// /// verifica autorizzazione al canale, ovvero /// - deve avere tutte le chiavi/registri di comunicazione /// - CodImpiego e AppKey devono essere validate /// public bool ChannelAuth { get { // in primis deve avere i dati di configurazione bool answ = CurrCheck.CurrPatrolCont.HasCommData; if (answ) { // verifica sia valida (scadenza a tempo da ultimo controllo) chiave auth in remoto (cod impiego + AuthKey ...) // FixMe ToDo !!! } return answ; } } public bool CloudCallActive { get => cloudCallActive; set { if (cloudCallActive != value) { cloudCallActive = value; ReportRemoteCall(); } } } public string CodApp { get => CurrCheck.CurrPatrolCont.CodApp; set { if (CurrCheck.CurrPatrolCont.CodApp != value) { // verifico ammissibilità CurrCheck.CurrPatrolCont.CodApp = value; ReportConfigUpd(); } } } /// /// Dir applicazione /// public string DataDir { get; set; } = ""; /// /// ABilitazione glocale notifiche da conf /// public bool EnableNotify { get => CurrCheck.CurrPatrolCont.EnableNotify; } public List ListAppStatus { get => CurrCheck.ListAppStatus; set { CurrCheck.ListAppStatus = value; ReportStatusUpd(); } } public string MainKey { get => CurrCheck.CurrPatrolCont.MainKey; set { if (CurrCheck.CurrPatrolCont.MainKey != value) { // verifico ammissibilità CurrCheck.CurrPatrolCont.MainKey = value; ReportConfigUpd(); } } } public int RefreshPeriod { get => CurrCheck.CurrPatrolCont.RefreshIntSec; set { if (CurrCheck.CurrPatrolCont.RefreshIntSec != value) { // verifico ammissibilità CurrCheck.CurrPatrolCont.RefreshIntSec = value < refPMin ? refPMin : value > refPMax ? refPMax : value; ReportConfigUpd(); } } } public List TargetList { get => CurrCheck.CurrPatrolCont.TargetList; set { CurrCheck.CurrPatrolCont.TargetList = value; ReportConfigUpd(); } } /// /// num minuti veto controllo COMPLETO /// public int VetoCheck { get => CurrCheck.CurrPatrolCont.VetoCheckMinutes; set { if (CurrCheck.CurrPatrolCont.VetoCheckMinutes != value) { // verifico ammissibilità CurrCheck.CurrPatrolCont.VetoCheckMinutes = value < vetoPMin ? vetoPMin : value > vetoPMax ? vetoPMax : value; ReportConfigUpd(); } } } /// /// num minuti veto controllo refresh /// public int VetoRefresh { get => CurrCheck.CurrPatrolCont.VetoRefreshMinutes; } #endregion Public Properties #region Public Methods /// /// Verifica se sia stato assegnato il codice licenza x una richiesta di enroll e nel caso recupera licenza e restituisce il codice da salvare in conf /// /// public async Task CheckAssignIdxLic() { string tVal = ""; // verifico dati richeista enroll, altrimenti faccio refresh richiesta... if (CurrEnrollData.IdReq == 0) { int newCode = await GetAuthPassocde(); } CloudCallActive = true; // recupero info EnrollRequestDTO updRec = await CurrCheck.CheckCurrEnroll(); if (updRec.IdReq > 0) { CurrEnrollData = updRec; // se la licenza è valida --> procedo! if (updRec.IdxLic > 0) { LicenzaDTO currLic = await CurrCheck.GetEnrollLic(); // se licenza valida --> restituisco e salvo! if (currLic.IsValid && currLic.IsActive && !string.IsNullOrEmpty(currLic.Chiave)) { tVal = currLic.Chiave; CurrCheck.CurrPatrolCont.MainKey = currLic.Chiave; // Ricalcolo valori subLic... ResetSubLic(true); await Task.Delay(10); // verifico attivazioni bool fatto = await CheckAttivazioni(); // ... e salvo DoSaveConfig(); } } } CloudCallActive = false; return tVal; } /// /// Chiamata verifica attivazioni /// /// public async Task CheckAttivazioni() { Stopwatch sw = new Stopwatch(); sw.Start(); bool fatto = await CurrCheck.CheckAttivazioni(); sw.Stop(); StatsCollector.UpdateStat($"CheckAttivazioni", sw.Elapsed); return fatto; } /// /// Effettua un controllo completo (locale e remoto) /// /// se true esegue anche prima della scadenza veto public async Task DoFullCheckAsync(bool doForce) { DateTime adesso = DateTime.Now; Stopwatch sw = new Stopwatch(); sw.Start(); if (lastCheckDone.AddMinutes(VetoRefresh) < adesso || doForce) { CloudCallActive = true; lastCheckDone = adesso; // se non è forzato controllo ultimo check // in primis controllo se il remote è ok sennò mi fermo... bool remoteOk = await CurrCheck.CheckRemote(); if (remoteOk) { await CurrCheck.SetManaged(); // effettua un refresh del controllo status... bool locCheckOk = CurrCheck.UpdateLocalStatus(doForce); bool remCheckOk = false; bool critCheckOk = false; if (locCheckOk) { remCheckOk = await CurrCheck.CheckRemoteReleases(); } // infine effettua una verifica dello status "release critiche" critCheckOk = await CurrCheck.CheckCriticalReport(); if (remCheckOk || critCheckOk) { ReportStatusUpd(); } } CloudCallActive = false; // predispongo x check task successivo... vetoTaskCheck = adesso.AddSeconds(-1); // Collezione statistiche esecuzione sw.Stop(); string checkType = doForce ? "Forced" : "Standard"; StatsCollector.UpdateStat($"AppCheck{checkType}", sw.Elapsed); // invio statistiche esecuzione... await DoSendRunStats(); } } /// Effettua rilettura configurazione e setup controlli... /// Effettua salvataggio configurazione /// public void DoSaveConfig() { try { var rawData = JsonConvert.SerializeObject(CurrCheck.CurrPatrolCont, Formatting.Indented); if (rawData != null && rawData.Length > 2) { File.WriteAllText(CurrCheck.ConfPath, rawData); Log.Trace($"Effettuato salvataggio Config Setup! | {ConfDir} | {DataDir}"); } } catch (Exception exc) { Log.Equals($"Eccezione in DoSaveConfig{Environment.NewLine}{exc}"); } ReportConfigUpd(); } /// /// Effettua un controllo dei task da eseguire /// /// se true esegue anche prima della scadenza veto public async Task DoTaskCheckAsync(bool doForce) { Stopwatch sw = new Stopwatch(); sw.Start(); DateTime adesso = DateTime.Now; bool sendStats = false; if (vetoTaskCheck < adesso || doForce || numFastCheck > 0) { // veto random prossimo controllo default 2 min (media)... double vetoSec = (double)rnd.Next(10000, 14000) / 100; if (numFastCheck > 0) { numFastCheck--; } // verifica se ci siano task da eseguire... var task2exe = await CurrCheck.TaskGetReq(DeviceName); if (task2exe != null && task2exe.Count > 0) { CloudCallActive = true; // in primis li segnala in running... await CurrCheck.TaskSetRunningAsync(DeviceName, task2exe); // imposto limite a scalare x i prox fast check avendo trovato task da eseguire... numFastCheck = RuntimeConfInt("CountNumFastCheck"); Dictionary taskResults = new Dictionary(); // eseguo 1:1 ... foreach (var currTask in task2exe) { var cResp = await ExecuteTask(currTask); // provo ad accodare risposte... foreach (var resp in cResp) { if (taskResults.ContainsKey(resp.Key)) { taskResults[resp.Key] = resp.Value; } else { taskResults.Add(resp.Key, resp.Value); } } } // invio risposta esito esecuzione finale await CurrCheck.TaskSetDoneAsync(DeviceName, taskResults); sendStats = true; CloudCallActive = false; } // imposto veto controlli task vetoTaskCheck = adesso.AddSeconds(vetoSec); // Collezione statistiche esecuzione sw.Stop(); StatsCollector.UpdateStat($"TaskCheck", sw.Elapsed); if (sendStats) { // invio statistiche esecuzione... await DoSendRunStats(); } } } /// /// Restituisce un codice di auth temporaneo INT da impiegare x autorizzare /// /// public async Task GetAuthPassocde() { int tVal = 0; //verifico che NON ci sia una richiesta già in corso... if (CurrEnrollData.IdReq == 0) { Dictionary reqInfo = DeviceInfoDict(); CloudCallActive = true; // faccio la chiamata rest e salvo risposta... CurrEnrollData = await CurrCheck.GetNewEnroll(reqInfo); CloudCallActive = false; } // risposta valida se trovo un id richeista > 0... if (CurrEnrollData.IdReq > 0) { tVal = CurrEnrollData.Passcode; } return tVal; } /// /// Metodo update specifico x app IobWin a step /// /// /// Step richiesto: 1=backup conf, 2=download, 3=unzip, $=fix conf e close /// public async Task IobWinUpdateStep(VersStatusDTO reqApp, int step) { Stopwatch sw = new Stopwatch(); sw.Start(); // eseguo step secondo richiesta... switch (step) { case 1: await IobWinDoBackup(reqApp); break; case 2: await IobWinDownload(reqApp); break; case 3: await IobWinUnzip(reqApp); break; case 4: await IobWinRestoreConf(reqApp); break; default: break; } sw.Stop(); StatsCollector.UpdateStat($"AppUpdate", sw.Elapsed); } public void ReportStatusUpd() { if (EA_StatusUpdated != null) { #if false // controllo se almeno 1 app abbia update... foreach (var item in ListAppStatus) { if (item.HasUpdate) { EA_StatusUpdated?.Invoke(); break; } } #endif EA_StatusUpdated?.Invoke(); } } public void ResetConf() { bool fatto = CurrCheck.ResetConf(); if (fatto) { ReportConfigUpd(); ReportStatusUpd(); } } /// /// Reimposta CodImpiego / AppKey da librerie Egw secondo richiesta... /// /// public void ResetSubLic(bool resAppKey) { if (resAppKey) { CurrCheck.CurrPatrolCont.AppKey = SubLicManager.GenKey(CodApp); } ReportConfigUpd(); } /// /// Invia info configurazione (directory) /// /// public async Task SendConfTarget() { // preparo un task di conf da inviare var task2send = new Dictionary(); string rawConf = JsonConvert.SerializeObject(CurrCheck.CurrPatrolCont.TargetList); task2send.Add("TargetList", rawConf); await CurrCheck.TaskSetDoneAsync(DeviceName, task2send); } /// /// Invia info licenza (se disponibili) /// /// public async Task SendLicInfo() { if (CurrCheck.LicenceFilesDict.Count > 0) { // serializzo risultato string rawData = JsonConvert.SerializeObject(CurrCheck.LicenceFilesDict); Dictionary licInfo = new Dictionary(); // chiave: LicInfo licInfo.Add("LicInfo", rawData); // invio! await CurrCheck.TaskSetDoneAsync(DeviceName, licInfo); } } /// /// Invia immediatamente info di reboot effettuato (spostando da req/run a done...) /// /// public void SendRebooted(bool sendReboot) { // preparo un task di reboot da inviare var task2send = new Dictionary(); task2send.Add("ExecStart", $"{DateTime.Now:yyyy-MM-dd HH:mm:ss}"); CurrCheck.TaskSetRunning(DeviceName, task2send); if (sendReboot) { // aggiungo anche rebooted... task2send.Add("RebootRequest", $"{DateTime.Now:yyyy-MM-dd HH:mm:ss}"); } CurrCheck.TaskSetDone(DeviceName, task2send); } /// /// Invia info di reboot effettuato (spostando da req/run a done...) /// /// public async Task SendRebootedAsync(bool sendReboot) { // preparo un task di reboot da inviare var task2send = new Dictionary(); task2send.Add("ExecStart", $"{DateTime.Now:yyyy-MM-dd HH:mm:ss}"); await CurrCheck.TaskSetRunningAsync(DeviceName, task2send); await Task.Delay(50); if (sendReboot) { // aggiungo anche rebooted... task2send.Add("RebootRequest", $"{DateTime.Now:yyyy-MM-dd HH:mm:ss}"); } await CurrCheck.TaskSetDoneAsync(DeviceName, task2send); } /// /// Invia info satats preliminari /// /// public async Task SendStats() { // invio statistiche esecuzione... await DoSendRunStats(); } #endregion Public Methods #region Protected Fields protected string DeviceName = ""; protected UpdateMan updateMan = new UpdateMan(); protected UpdateMan updateManAuth = new UpdateMan("SWDownloader", "viaD@nte16"); #endregion Protected Fields #region Protected Properties protected SubLicManager SubLicManager { get; set; } = new SubLicManager(); #endregion Protected Properties #region Protected Methods /// /// Helper copia directory + contenuto /// /// /// protected void CopyDirectory(string sourceDir, string destDir) { // Create the destination directory if it doesn't exist if (!Directory.Exists(destDir)) { Directory.CreateDirectory(destDir); } // copio SOLO SE ho la dir di partenza if (Directory.Exists(sourceDir)) { // Copy all files foreach (var file in Directory.GetFiles(sourceDir)) { string destFile = Path.Combine(destDir, Path.GetFileName(file)); File.Copy(file, destFile, true); } // Copy all subdirectories foreach (var dir in Directory.GetDirectories(sourceDir)) { string destSubDir = Path.Combine(destDir, Path.GetFileName(dir)); CopyDirectory(dir, destSubDir); } } } /// /// Esegue recupero dati licenze oxysec e li invia a LiMan /// /// protected async Task DoSendLicInfo() { // chiamo metodo di update licenze... var hasLic = CurrCheck.UpdateLicenceInfo(); if (hasLic) { // serializzo risultato string rawData = JsonConvert.SerializeObject(CurrCheck.LicenceFilesDict); Dictionary licInfo = new Dictionary(); // chiave: LicInfo licInfo.Add("LicInfo", rawData); // invio! await CurrCheck.TaskSetDoneAsync(DeviceName, licInfo); } } /// /// Esegue recupero dati statistiche esecuzione e li invia a LiMan /// /// protected async Task DoSendRunStats() { // recupero statistiche Dictionary statsData = StatsCollector.CurrentInfo(); // aggiungo versione alle statistiche statsData.Add("Version", $"{mainAssembly.GetName().Version}"); //serializzo string rawData = JsonConvert.SerializeObject(statsData); Dictionary statsInfo = new Dictionary(); // chiave: RunStats statsInfo.Add("RunStats", rawData); // invio! await CurrCheck.TaskSetDoneAsync(DeviceName, statsInfo); } #endregion Protected Methods #region Private Fields /// /// Classe logger /// private static Logger Log = LogManager.GetCurrentClassLogger(); /// /// Directory di backup x IobWin /// private string backupDir = @"C:\Steamware\backup"; /// /// DataOra ultimo controllo effettuato /// private DateTime lastCheckDone = DateTime.Today.AddMonths(-1); private Assembly mainAssembly = Assembly.GetExecutingAssembly(); /// /// Numero di controlli fast (5 sec medi) dei task prima di tornare alla gestione lenta (2 min) /// private int numFastCheck = 0; /// /// Valore massimo ammesso refresh sec /// private int refPMax = 3600; /// /// Valore minimo ammesso refresh sec /// private int refPMin = 1; private Random rnd = new Random(); /// /// Configurazione parametri speciali a runtime (con override da remoto...) /// private Dictionary RuntimeConfDict = new Dictionary(); /// /// Directory temp di appoggio /// private string tempDir = @"C:\Steamware\temp"; /// /// File segnaposto x lock fasi di update /// private string updFilePath = @"c:\Steamware\IOB-MAN\update.run"; /// /// Valore massimo ammesso veto minuti /// private int vetoPMax = 14400; /// /// Valore minimo ammesso veto minuti /// private int vetoPMin = 30; /// /// DataOra ultimo controllo della parte TASK da eseguire /// private DateTime vetoTaskCheck = DateTime.Today; /// /// Ms di attesa x uscita processo (std) /// private int waitForExitMsec = 250; #endregion Private Fields #region Private Properties private bool cloudCallActive { get; set; } = false; private string CodImpiego { get; set; } = ""; private string ConfDir { get; set; } = ""; private ReleaseChecker CurrCheck { get; set; } = null!; /// /// Richiesta enroll corrente (se presente come file oppure nuova...) /// private EnrollRequestDTO CurrEnrollData { get => CurrCheck.CurrEnrollData; set => CurrCheck.CurrEnrollData = value; } private SubLicManager SLicManager { get; set; } = new SubLicManager(); #endregion Private Properties #region Private Methods /// /// Predispone un dizionario delle info del device + ID x inviare /// /// private static Dictionary DeviceInfoDict() { // in primis preparo dizionario dei dati da allegare alla richiesta Dictionary reqInfo = new Dictionary(); // in primis setup dati dizionario conf... if (DictUserInfo == null || DictUserInfo.Count == 0) { DictUserInfo = new Dictionary(MachineDataValidator.userInfo); } if (DictNetInfo == null || DictNetInfo.Count == 0) { DictNetInfo = new Dictionary(MachineDataValidator.netInfo); } if (DictMachineInfo == null || DictMachineInfo.Count == 0) { DictMachineInfo = new Dictionary(DictUserInfo); foreach (var item in DictNetInfo) { if (!DictMachineInfo.ContainsKey(item.Key)) { DictMachineInfo.Add(item.Key, item.Value); } } } // Iniziallizzo con dati MachineInfo... reqInfo = new Dictionary(DictMachineInfo); foreach (var item in new Dictionary(MachineDataValidator.netInfo)) { if (!reqInfo.ContainsKey(item.Key)) { reqInfo.Add(item.Key, item.Value); } } // ora i dati DeviceId da allegare in coda string deviceId = new DeviceIdBuilder() .AddMachineName() .AddUserName() .AddOsVersion() .OnWindows(windows => windows .AddMacAddressFromWmi(excludeWireless: true, excludeNonPhysical: true) .AddProcessorId() .AddMotherboardSerialNumber() .AddSystemDriveSerialNumber()) .ToString(); // raggiungo DevideID reqInfo.Add("DeviceID", deviceId); return reqInfo; } /// /// Elimina contenuto directory /// /// private void DeleteDirectoryContents(string directoryPath) { // recupero info dir DirectoryInfo directory = new DirectoryInfo(directoryPath); // elimina file foreach (FileInfo file in directory.GetFiles()) { file.Delete(); } // elimina ulteriori directory foreach (DirectoryInfo subDirectory in directory.GetDirectories()) { subDirectory.Delete(true); } } /// /// Esecuzione vero e proprio task richiesto /// /// /// private async Task> ExecuteTask(KeyValuePair item) { Dictionary taskDone = new Dictionary(); bool taskOk = false; string taskVal = ""; Stopwatch sw = Stopwatch.StartNew(); // converto richiesta in enum... CoreEnum.EgwAccTask tName = CoreEnum.EgwAccTask.ND; Enum.TryParse(item.Key, out tName); // switch sui vari casi x eseguire... switch (tName) { //case CoreEnum.EgwAccTask.AppSearch: // break; case CoreEnum.EgwAccTask.BackupSendConf: Dictionary bsRes = new Dictionary(); bsRes.Add("ExecStart", $"{DateTime.Now:yyyy-MM-dd HH:mm:ss}"); await IobConfZipUpload(); sw.Stop(); taskVal = JsonConvert.SerializeObject(bsRes); break; case CoreEnum.EgwAccTask.DeviceInfoGet: Dictionary currDevInfo = DeviceInfoDict(); taskVal = JsonConvert.SerializeObject(currDevInfo); break; case CoreEnum.EgwAccTask.ForceCheck: Dictionary fcRes = new Dictionary(); fcRes.Add("ExecStart", $"{DateTime.Now:yyyy-MM-dd HH:mm:ss}"); await DoFullCheckAsync(true); sw.Stop(); fcRes.Add("Completed", $"App Check done in {sw.Elapsed.TotalMilliseconds:N0}ms"); taskVal = JsonConvert.SerializeObject(fcRes); break; case CoreEnum.EgwAccTask.ForceReload: Dictionary frRes = new Dictionary(); frRes.Add("ExecReload", $"{DateTime.Now:yyyy-MM-dd HH:mm:ss}"); sw.Stop(); taskVal = JsonConvert.SerializeObject(frRes); // sollevo evento if (EA_ReloadRequested != null) { EA_ReloadRequested?.Invoke(); } break; case CoreEnum.EgwAccTask.ForceUpdate: Dictionary fuRes = new Dictionary(); fuRes.Add("ExecStart", $"{DateTime.Now:yyyy-MM-dd HH:mm:ss}"); sw.Stop(); // mando subito conferma esecuzione... taskVal = JsonConvert.SerializeObject(fuRes); RaiseRestartReq(); break; case CoreEnum.EgwAccTask.OxyLicenseGet: Dictionary olgRes = new Dictionary(); await DoSendLicInfo(); sw.Stop(); // mando subito conferma esecuzione... olgRes.Add("LicInfoCompleted", $"{DateTime.Now:yyyy-MM-dd HH:mm:ss}"); taskVal = JsonConvert.SerializeObject(olgRes); break; //case CoreEnum.EgwAccTask.ParamDictReset: // break; //case CoreEnum.EgwAccTask.ParamUpsert: // break; case CoreEnum.EgwAccTask.TargetListUpsert: // verifico se nel payload c'è la configurazione VALIDA... if (string.IsNullOrEmpty(item.Value)) { taskVal = "Error: empty Payload"; } else { // provo a deserializzare oggetto var newTargetList = JsonConvert.DeserializeObject>(item.Value); if (newTargetList == null || newTargetList.Count == 0) { taskVal = "Error: empty List"; } else { // se valido sovrascrivo e salvo! CurrCheck.CurrPatrolCont.TargetList = newTargetList; CurrCheck.SaveConfig(); // ... invio versione aggiornata await Task.Delay(100); await SendConfTarget(); } } break; case CoreEnum.EgwAccTask.TargetAppUpdate: // verifico se nel payload c'è valore dell'applciazione da aggiornare... if (string.IsNullOrEmpty(item.Value)) { taskVal = "Error: missing CodApp"; } else { CloudCallActive = true; // in primis chiamo aggiornamento stati release await DoFullCheckAsync(true); // il valore è il nome dell'app da aggiornare... string codAppReq = item.Value; // verifico da conf che la richiesta sia sensata (update x un app consentita...) var ReqApp = ListAppStatus.FirstOrDefault(x => x.CodApp == codAppReq); // se ho trovato... if (ReqApp != null) { //verifica ulteriore tipo app e con update... limitato a WinApp... if (ReqApp != null && ReqApp.ApplicationType == CoreEnum.AppType.WinApp && ReqApp.HasUpdate) { // esegue update delle app Iob-* // "Backup Config" await IobWinUpdateStep(ReqApp, 1); // "Download update"; await IobWinUpdateStep(ReqApp, 2); // "Unzip software"; await IobWinUpdateStep(ReqApp, 3); // "Config Restore"; await IobWinUpdateStep(ReqApp, 4); // update finale await DoFullCheckAsync(true); } else { taskVal = $"Update non permesso per {codAppReq}"; } } else { taskVal = $"App non trovatao: {codAppReq}"; } CloudCallActive = false; ReportStatusUpd(); } break; case CoreEnum.EgwAccTask.ND: default: taskVal = $"taskReq: {tName} | key: {item.Key} | val: {item.Value} | SKIPPED | NO EXEC"; Log.Info($"Chiamata senza processing: taskOk: {taskOk} | taskVal: {taskVal}"); break; } // aggiungo task! taskDone.Add(item.Key, taskVal); // aggiungo task ultima esecuzione... Dictionary finDet = new Dictionary(); finDet.Add("Completed", $"{DateTime.Now:yyyy-MM-dd HH:mm:ss}"); taskVal = JsonConvert.SerializeObject(finDet, Formatting.Indented); taskDone.Add("LastTaskExec", taskVal); StatsCollector.UpdateStat($"Exec{item.Key}", sw.Elapsed); return taskDone; } /// /// Solleva evento richiesta restart /// public void RaiseRestartReq() { // sollevo evento if (EA_RestartRequested != null) { EA_RestartRequested?.Invoke(); } } /// /// Effettua un force kill dato nome processo /// /// private void ForceKillByName(string nomeProc) { Process[] stillRunningProc = Process.GetProcessesByName(nomeProc); if (stillRunningProc != null) { if (stillRunningProc.Length > 0) { foreach (var item in stillRunningProc) { try { Process p = Process.GetProcessById(item.Id); { if (!p.HasExited) { int closeWaitTime = waitForExitMsec * 8; // se è MAN --> aspetto + a lungo... if (nomeProc.Contains("IOB-MAN")) { closeWaitTime = closeWaitTime * 4; } p.CloseMainWindow(); p.WaitForExit(closeWaitTime); } if (!p.HasExited) { Log.Error($"Process not Exited, 2nd try p.kill()"); p.Kill(); p.WaitForExit(waitForExitMsec); } if (!p.HasExited) { Log.Error($"Process not Killed, 3nd try p.kill()"); p.Kill(); p.WaitForExit(waitForExitMsec * 2); } if (!p.HasExited) { Log.Error($"Process not Killed, 4th try p.kill()"); p.Kill(); } } } catch (Exception exc) { Log.Error($"Errore in fase di kill processo da nome {exc}"); } } } } } /// /// Effettua zip della folder di backup conf e la invia a LiMan /// /// private async Task IobConfZipUpload() { // controllo che ci sia la firectory di partenza... string confDir = Path.Combine(backupDir, "Conf"); if (Directory.Exists(confDir)) { // preparo dir temporanea x zip string tempZip = Path.Combine(tempDir, "ConfCopy"); if (Directory.Exists(tempZip)) { Directory.Delete(tempZip, true); } Directory.CreateDirectory(tempZip); // copio contenuto dir conf CopyDirectory(confDir, tempZip); // preparo zip string zipPath = Path.Combine(tempDir, "IOB_Conf.zip"); if (File.Exists(zipPath)) { File.Delete(zipPath); } else { string zipDir = Path.GetDirectoryName(zipPath) ?? ""; if (!string.IsNullOrEmpty(zipDir)) { if (!Directory.Exists(zipDir)) { Directory.CreateDirectory(zipDir); } } } await Task.Run(() => { ZipFile.CreateFromDirectory(tempZip, zipPath, CompressionLevel.Optimal, false); }); // chiamo metodo x upload zip bool uploaded = await CurrCheck.UploadZipfile(AppKey, zipPath); // elimino dir di appoggio if (Directory.Exists(tempZip)) { Directory.Delete(tempZip, true); } } await Task.Delay(10); } /// /// Step preliminare di backup conf e app corrente /// /// /// private async Task IobWinDoBackup(VersStatusDTO reqApp) { // controllo di avere un file di riferimento... string appDir = Path.GetDirectoryName(reqApp.LocalPath) ?? ""; if (!string.IsNullOrEmpty(appDir)) { string tempZip = Path.Combine(tempDir, "FULL", reqApp.CodApp); // step 0: inserisce i file semaforo di segnalazione update in corso string manDir = Path.GetDirectoryName(updFilePath) ?? ""; if (!string.IsNullOrEmpty(manDir)) { if (!Directory.Exists(manDir)) { Directory.CreateDirectory(manDir); } } using (FileStream fs = File.Create(updFilePath)) { // File is created and access is released when the using block ends } // step 1: backup configurazioni partendo da path applicazione... string srcDir = Path.Combine(appDir, "DATA", "CONF"); string destDir = Path.Combine(backupDir, "Conf", reqApp.CodApp, "DATA", "CONF"); // verifico se esista la srcDir, altrimenti uso solo CONF... if (!Directory.Exists(srcDir)) { srcDir = Path.Combine(appDir, "CONF"); destDir = Path.Combine(backupDir, "Conf", reqApp.CodApp, "CONF"); } CopyDirectory(srcDir, destDir); // step 2a: kill log... ForceKillByName("TailBlazer"); ForceKillByName("Tail Blazer"); // step 2b: kill processi ForceKillByName(reqApp.CodApp); // step 3: backup + zip intera folder applicativo if (Directory.Exists(tempZip)) { Directory.Delete(tempZip, true); } Directory.CreateDirectory(tempZip); CopyDirectory(appDir, tempZip); string zipPath = Path.Combine(backupDir, "Archive", reqApp.CodApp, $"{DateTime.Now:yyyyMMdd}_{DateTime.Now:HHmmss}.zip"); if (File.Exists(zipPath)) { File.Delete(zipPath); } else { string zipDir = Path.GetDirectoryName(zipPath) ?? ""; if (!string.IsNullOrEmpty(zipDir)) { if (!Directory.Exists(zipDir)) { Directory.CreateDirectory(zipDir); } } } await Task.Run(() => { ZipFile.CreateFromDirectory(tempZip, zipPath); }); } await Task.Delay(10); } /// /// Step di download da remoto /// /// /// private async Task IobWinDownload(VersStatusDTO reqApp) { // step 4: download // controllo di avere un file di riferimento... string appDir = Path.GetDirectoryName(reqApp.LocalPath) ?? ""; if (!string.IsNullOrEmpty(appDir)) { string installerDir = Path.Combine(backupDir, "installer", reqApp.CodApp); // se il path fosse vuoto --> uso quello standard da nexus... string urlDownload = reqApp.UrlDownload; if (string.IsNullOrEmpty(urlDownload)) { urlDownload = $"https://nexus.steamware.net/repository/utility/MAPO/{reqApp.CodApp}/stable/{reqApp.CodApp}.zip"; } long size = updateMan.DownloadApp(urlDownload, installerDir, reqApp.CodApp, reqApp.VersNumLast); // se non andata provo con auth... if (size == 0) { size = updateManAuth.DownloadApp(urlDownload, installerDir, reqApp.CodApp, reqApp.VersNumLast); } } await Task.Delay(10); } /// /// Step di fix backup configurazioni + unlock file update run /// /// /// private async Task IobWinRestoreConf(VersStatusDTO reqApp) { // step 6: restore conf string appDir = Path.GetDirectoryName(reqApp.LocalPath) ?? ""; if (!string.IsNullOrEmpty(appDir)) { string srcDir = Path.Combine(appDir, "DATA", "CONF"); string destDir = Path.Combine(backupDir, "Conf", reqApp.CodApp, "DATA", "CONF"); if (!Directory.Exists(destDir)) { srcDir = Path.Combine(appDir, "CONF"); destDir = Path.Combine(backupDir, "Conf", reqApp.CodApp, "CONF"); } CopyDirectory(destDir, srcDir); } // step 7: rimozione file lock semaforo File.Delete(updFilePath); await Task.Delay(10); } /// /// Step di unzip file scaricato /// /// /// private async Task IobWinUnzip(VersStatusDTO reqApp) { // step 5: unzip + overwrite string appDir = Path.GetDirectoryName(reqApp.LocalPath) ?? ""; if (!string.IsNullOrEmpty(appDir)) { string installerDir = Path.Combine(backupDir, "installer", reqApp.CodApp); string srcZip = Path.Combine(installerDir, $"{reqApp.CodApp}.zip"); // svuoto la dir di destinazione preliminarmente... DeleteDirectoryContents(appDir); // esegue unzip ZipFile.ExtractToDirectory(srcZip, appDir, true); } await Task.Delay(10); } private void ReportConfigUpd() { if (EA_ConfigUpdated != null) { EA_ConfigUpdated?.Invoke(); } } private void ReportRemoteCall() { if (EA_RemoteCalling != null) { EA_RemoteCalling?.Invoke(); } } /// /// Recupera parametro conf in Runtime di tipo Int /// /// /// private int RuntimeConfInt(string param) { int answ = 0; if (RuntimeConfDict.ContainsKey(param)) { string retVal = RuntimeConfDict[param]; int.TryParse(retVal, out answ); } return answ; } /// /// Recupera parametro conf in Runtime di tipo Int /// /// /// private string RuntimeConfString(string param) { string answ = ""; if (RuntimeConfDict.ContainsKey(param)) { answ = RuntimeConfDict[param]; } return answ; } #endregion Private Methods } }