using Newtonsoft.Json; using Step.Model.DTOModels.JobModels; using System; using System.Collections.Generic; using System.Drawing; using System.Drawing.Imaging; using System.IO; using System.IO.Compression; using System.Linq; using System.Reflection; using System.Security.Cryptography; using System.Text; using System.Text.RegularExpressions; using static CMS_CORE_Library.Models.DataStructures; using static Step.Model.Constants; namespace Step.Utils { public static class SupportFunctions { private static readonly string CMSCONNECT_TOKEN = "59f1qik5PYfiJLiXZ4xZ05pjzx5E9FscQWtj4lmfLKXaF1OAxkvu7ziBzXFBuuVQ"; public static SOFTKEY_TYPE GetSoftKeyType(string type) { switch (type) { case "softKey_procedure": return SOFTKEY_TYPE.PROCEDURE; case "softKey_group": return SOFTKEY_TYPE.GROUP; default: return SOFTKEY_TYPE.TOGGLE; } } public static HEAD_TYPE GetHeadType(string type) { switch (type) { case "SPINDLE": return HEAD_TYPE.SPINDLE; case "AWJ": return HEAD_TYPE.AWJ; case "ADDITIVE": return HEAD_TYPE.ADDITIVE; case "CUTTING_GLASS": return HEAD_TYPE.CUTTING_GLASS; default: return HEAD_TYPE.WJ; } } public static int GetPlcIdFromNcSoftKey(string softKey) { switch (softKey) { case "auto": return 1; case "edit": return 2; case "mdi": return 3; case "dnc": return 4; case "ref": return 5; case "jog": return 6; case "jogInc": return 7; case "restart": return 8; case "teach": return 9; case "retract": return 10; case "wcsMcs": return 11; case "handle": return 12; case "reset": return 13; case "blk": return 14; case "blkDel": return 15; case "opStop": return 16; case "dryRun": return 17; case "prgTest": return 18; case "manualHandleInterrupt": return 19; case "teachIn": return 20; case "incPlane": return 21; case "plus": return 22; case "minus": return 23; case "rapid": return 24; case "xOne": return 25; case "xTen": return 26; case "xHundred": return 27; case "xThousand": return 28; case "overstroke": return 30; case "feedByPass": return 31; default: return -1; } } public static double ConvertInMinutes(double number, MAINTENANCE_UNIT_OF_MEASURE unit) { switch (unit) { case MAINTENANCE_UNIT_OF_MEASURE.mm: return number; case MAINTENANCE_UNIT_OF_MEASURE.H: return number * 60; case MAINTENANCE_UNIT_OF_MEASURE.D: return number * (24 * 60); case MAINTENANCE_UNIT_OF_MEASURE.M: return (30 * number) * (24 * 60); default: return number; } } public static double ConvertInUmeas(double number, MAINTENANCE_UNIT_OF_MEASURE unit) { switch (unit) { case MAINTENANCE_UNIT_OF_MEASURE.mm: return number; case MAINTENANCE_UNIT_OF_MEASURE.H: return number / 60; case MAINTENANCE_UNIT_OF_MEASURE.D: return number / (24 / 60); case MAINTENANCE_UNIT_OF_MEASURE.M: return (30 / number) / (24 / 60); default: return number; } } public static void CopyProperties(TParent parent, TChild child) where TParent : class where TChild : class { var parentProperties = parent.GetType().GetProperties(); var childProperties = child.GetType().GetProperties(); foreach (var parentProperty in parentProperties) { foreach (var childProperty in childProperties) { if (parentProperty.Name == childProperty.Name && parentProperty.PropertyType == childProperty.PropertyType) { childProperty.SetValue(child, parentProperty.GetValue(parent)); break; } } } } public static int GetNextId(IEnumerable objIds) { if (objIds.Count() == 0) return 1; return objIds.Max() + 1; } public static short GetFirstFreeId(IEnumerable objIds) { if (objIds.Count() == 0) return 1; List listOfPossibleIds = Enumerable.Range(1, objIds.Max() + 1).Select(x => (short)x).ToList(); IEnumerable res = listOfPossibleIds.Except(objIds).ToList(); if (res.Count() > 0) return res.First(); else return 1; } public static string GetImageBase64String(string directoryPath, string imageName) { string fileName = Path.GetFileNameWithoutExtension(imageName); foreach (string ext in VALID_IMAGE_EXTENSIONS) { if (File.Exists(directoryPath + "\\" + fileName + ext)) { // Convert image to a base 64 string return "data:image/" + ext + ";base64," + Convert.ToBase64String(File.ReadAllBytes(directoryPath + "\\" + fileName + ext)); } } return ""; } public static string ExtractBase64ProgIcon(string path) { if (!File.Exists(path)) return ""; Image im = Icon.ExtractAssociatedIcon(path)?.ToBitmap(); MemoryStream m = new MemoryStream(); im.Save(m, ImageFormat.Png); return "data:image/png;base64," + Convert.ToBase64String(m.ToArray()); } public static DTOJobModel UnpackJobAndReadMetadata(string filePath, int processId) { DTOJobModel job = new DTOJobModel(); string jobFolderPath = JOB_TMP_DIRECTORY + "\\" + processId + "\\"; if (!Directory.Exists(jobFolderPath)) Directory.CreateDirectory(jobFolderPath); // Check if job exists if (!File.Exists(filePath)) return null; EmptyFolder(jobFolderPath); using (ZipArchive zipExtractor = ZipFile.OpenRead(filePath)) { // Setup main job fields job.Name = Path.GetFileName(filePath); job.LastEditTimestamp = new FileInfo(filePath).LastAccessTime; foreach (ZipArchiveEntry entry in zipExtractor.Entries) { // Extract file entry.ExtractToFile(jobFolderPath + entry.Name, true); // Get main program content if (entry.Name.Equals(JOB_MAIN_FILENAME, StringComparison.OrdinalIgnoreCase)) { using (var reader = new StreamReader(entry.Open())) job.IsoMainProgram = (reader.ReadToEnd()); } // Read images else if (VALID_IMAGE_EXTENSIONS.Contains(Path.GetExtension(entry.Name).ToLower())) { var bytes = default(byte[]); // Populate metadata using (var memstream = new MemoryStream()) { entry.Open().CopyTo(memstream); bytes = memstream.ToArray(); job.Metadata.Generics.Images.Add(new DTOImageParamModel() { Name = Path.GetFileNameWithoutExtension(entry.Name), Base64 = "data:image/" + Path.GetExtension(entry.Name).ToLower().TrimStart('.') + ";base64," + Convert.ToBase64String(bytes) }); } } // Read metadata else if (entry.Name.Equals(JOB_METADATA_FILENAME, StringComparison.OrdinalIgnoreCase)) { DTOMetadataFieldsModel metasFromFile = new DTOMetadataFieldsModel(); using (var reader = new StreamReader(entry.Open())) { metasFromFile = JsonConvert.DeserializeObject(reader.ReadToEnd()); if (metasFromFile == null) { return null; } else { job.Metadata.Generics.Description = metasFromFile.Description; job.Metadata.Generics.ExecutionTime = metasFromFile.ExecutionTime; job.Metadata.Tools = metasFromFile.Tools; job.Metadata.Customs = metasFromFile.Customs; } } } // Consider other file as part program else { job.PartPrograms.Add(jobFolderPath + entry.Name); } } return job; } } public static void EmptyFolder(string path) { DirectoryInfo di = new DirectoryInfo(path); foreach (FileInfo file in di.GetFiles()) file.Delete(); foreach (DirectoryInfo dir in di.GetDirectories()) dir.Delete(true); } public static bool IsValidJob(string filePath) { if (!File.Exists(filePath)) return false; try { using (var archive = ZipFile.OpenRead(filePath)) { // Find key files List files = archive .Entries .Where(x => x.FullName == JOB_MAIN_FILENAME || x.FullName == JOB_METADATA_FILENAME) .ToList(); // if there aren't both if (files.Count() < 2) return false; else return true; } } catch (Exception) { return false; } } public static DTOJobModel ReadExtractedJobMetadata(int processId) { string filePath = JOB_TMP_DIRECTORY + "\\" + processId + "\\"; if (!Directory.Exists(filePath)) return null; // Setup main Fields DTOJobModel jobData = new DTOJobModel() { Name = Path.GetFileName(filePath), LastEditTimestamp = new FileInfo(filePath).LastAccessTime }; DirectoryInfo di = new DirectoryInfo(filePath); foreach (FileInfo file in di.GetFiles()) { // Get main program content if (file.Name.Equals(JOB_MAIN_FILENAME, StringComparison.OrdinalIgnoreCase)) { jobData.IsoMainProgram = (File.ReadAllText(file.FullName)); } // Get images content without extract files else if (VALID_IMAGE_EXTENSIONS.Contains(Path.GetExtension(file.Name).ToLower())) { byte[] bytes = File.ReadAllBytes(file.FullName); jobData.Metadata.Generics.Images.Add(new DTOImageParamModel() { Name = file.Name, Base64 = "data:image/" + Path.GetExtension(file.Name).ToLower().TrimStart('.') + ";base64," + Convert.ToBase64String(bytes) }); } // Metadata else if (file.Name.Equals(JOB_METADATA_FILENAME, StringComparison.OrdinalIgnoreCase)) { DTOMetadataFieldsModel metasFromFile = new DTOMetadataFieldsModel(); metasFromFile = JsonConvert .DeserializeObject (File.ReadAllText(file.FullName)); if (metasFromFile == null) return null; else { jobData.Metadata.Generics.Description = metasFromFile.Description; jobData.Metadata.Generics.ExecutionTime = metasFromFile.ExecutionTime; jobData.Metadata.Tools = metasFromFile.Tools; jobData.Metadata.Customs = metasFromFile.Customs; } } } return jobData; } public static SCADA_MEM_TYPE GetMemTypeFromString(string memType) { memType = memType.ToUpper(); switch (memType) { case "BOOL": return SCADA_MEM_TYPE.BOOL; case "BYTE": return SCADA_MEM_TYPE.BYTE; case "WORD": return SCADA_MEM_TYPE.WORD; case "INT": return SCADA_MEM_TYPE.INT; case "REAL": return SCADA_MEM_TYPE.REAL; default: return SCADA_MEM_TYPE.BOOL; } } public static DateTime GetLinkerTime(Assembly assembly, TimeZoneInfo target = null) { var filePath = assembly.Location; const int c_PeHeaderOffset = 60; const int c_LinkerTimestampOffset = 8; var buffer = new byte[2048]; using (var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read)) stream.Read(buffer, 0, 2048); var offset = BitConverter.ToInt32(buffer, c_PeHeaderOffset); var secondsSince1970 = BitConverter.ToInt32(buffer, offset + c_LinkerTimestampOffset); var epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); var linkTimeUtc = epoch.AddSeconds(secondsSince1970); var tz = target ?? TimeZoneInfo.Local; var localTime = TimeZoneInfo.ConvertTimeFromUtc(linkTimeUtc, tz); return localTime; } public static string GetSoftwareVersionAndBuildDate() { Version v = Assembly.GetEntryAssembly()?.GetName().Version; if (v != null) { // Get first 2 number of the version var version = v.ToString().Split('.').Take(3).ToArray(); string betaString = IS_BETA ? " BETA " : " "; // Get Server version return $"{string.Join(".", version)}" + betaString + $"({GetLinkerTime(Assembly.GetEntryAssembly(), null).ToString("d")}" + $")"; } return ""; } public static Boolean DecodeCMSConnectGatewayLogin(string encryptedString, out string login, out string password) { login = ""; password = ""; //Check if is null if (String.IsNullOrEmpty(encryptedString)) return false; //Decode it String decodedLogin = StringCipher.Decrypt(encryptedString, CMSCONNECT_TOKEN); //Check if contains the Space if (!decodedLogin.Contains(" ")) return false; //Split it and check String[] tempLogin = decodedLogin.Split(' '); if (tempLogin.Length != 2) return false; //Set the variable login = tempLogin[0]; password = tempLogin[1]; return true; } public static Boolean EncodeCMSConnectGatewayLogin(out string encryptedString, string login, string password) { encryptedString = ""; //Check if contains the Space if (String.IsNullOrEmpty(login) || login.Contains(" ")) return false; //Check if contains the Space if (String.IsNullOrEmpty(password) || password.Contains(" ")) return false; encryptedString = StringCipher.Encrypt(login + " " + password, CMSCONNECT_TOKEN); return true; } public static string ConvertIntToAsciiString(int integer) { string outputString = ""; for (int i = 3; i >= 0; i--) { outputString += (char)((byte)(integer >> (8 * i))); } return outputString; } public static int ConvertAsciiStringToInt(string asciiString) { int number = 0; for (int i = 1; i <= asciiString.Count(); i++) number += asciiString[asciiString.Count() - i] << (8 * (i - 1)); return number; } public static void ConvertStringMachineNumberIntoNumber(string machineNumber, out bool containsLetters, out int intMachineVal) { intMachineVal = 0; containsLetters = Regex.IsMatch(machineNumber, @".*?[a-zA-Z].*?"); if (containsLetters) // Convert ASCII string to a single INT intMachineVal = SupportFunctions.ConvertAsciiStringToInt(machineNumber); else // Convert string of digits to a INT intMachineVal = int.Parse(machineNumber); } //----- Chipher Private Class --------------------------------- #region Chipher_Private_Class private static class StringCipher { // This constant is used to determine the keysize of the encryption algorithm in bits. // We divide this by 8 within the code below to get the equivalent number of bytes. private const int Keysize = 256; // This constant determines the number of iterations for the password bytes generation function. private const int DerivationIterations = 1000; public static string Encrypt(string plainText, string passPhrase) { // Salt and IV is randomly generated each time, but is preprended to encrypted cipher text // so that the same Salt and IV values can be used when decrypting. var saltStringBytes = Generate256BitsOfRandomEntropy(); var ivStringBytes = Generate256BitsOfRandomEntropy(); var plainTextBytes = Encoding.UTF8.GetBytes(plainText); using (var password = new Rfc2898DeriveBytes(passPhrase, saltStringBytes, DerivationIterations)) { var keyBytes = password.GetBytes(Keysize / 8); using (var symmetricKey = new RijndaelManaged()) { symmetricKey.BlockSize = 256; symmetricKey.Mode = CipherMode.CBC; symmetricKey.Padding = PaddingMode.PKCS7; using (var encryptor = symmetricKey.CreateEncryptor(keyBytes, ivStringBytes)) { using (var memoryStream = new MemoryStream()) { using (var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write)) { cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length); cryptoStream.FlushFinalBlock(); // Create the final bytes as a concatenation of the random salt bytes, the random iv bytes and the cipher bytes. var cipherTextBytes = saltStringBytes; cipherTextBytes = cipherTextBytes.Concat(ivStringBytes).ToArray(); cipherTextBytes = cipherTextBytes.Concat(memoryStream.ToArray()).ToArray(); memoryStream.Close(); cryptoStream.Close(); return Convert.ToBase64String(cipherTextBytes); } } } } } } public static string Decrypt(string cipherText, string passPhrase) { // Get the complete stream of bytes that represent: // [32 bytes of Salt] + [32 bytes of IV] + [n bytes of CipherText] var cipherTextBytesWithSaltAndIv = Convert.FromBase64String(cipherText); // Get the saltbytes by extracting the first 32 bytes from the supplied cipherText bytes. var saltStringBytes = cipherTextBytesWithSaltAndIv.Take(Keysize / 8).ToArray(); // Get the IV bytes by extracting the next 32 bytes from the supplied cipherText bytes. var ivStringBytes = cipherTextBytesWithSaltAndIv.Skip(Keysize / 8).Take(Keysize / 8).ToArray(); // Get the actual cipher text bytes by removing the first 64 bytes from the cipherText string. var cipherTextBytes = cipherTextBytesWithSaltAndIv.Skip((Keysize / 8) * 2).Take(cipherTextBytesWithSaltAndIv.Length - ((Keysize / 8) * 2)).ToArray(); using (var password = new Rfc2898DeriveBytes(passPhrase, saltStringBytes, DerivationIterations)) { var keyBytes = password.GetBytes(Keysize / 8); using (var symmetricKey = new RijndaelManaged()) { symmetricKey.BlockSize = 256; symmetricKey.Mode = CipherMode.CBC; symmetricKey.Padding = PaddingMode.PKCS7; using (var decryptor = symmetricKey.CreateDecryptor(keyBytes, ivStringBytes)) { using (var memoryStream = new MemoryStream(cipherTextBytes)) { using (var cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read)) { var plainTextBytes = new byte[cipherTextBytes.Length]; var decryptedByteCount = cryptoStream.Read(plainTextBytes, 0, plainTextBytes.Length); memoryStream.Close(); cryptoStream.Close(); return Encoding.UTF8.GetString(plainTextBytes, 0, decryptedByteCount); } } } } } } private static byte[] Generate256BitsOfRandomEntropy() { var randomBytes = new byte[32]; // 32 Bytes will give us 256 bits. using (var rngCsp = new RNGCryptoServiceProvider()) { // Fill the array with cryptographically secure random bytes. rngCsp.GetBytes(randomBytes); } return randomBytes; } } #endregion } }