///<summary>Gets the human readable host name of the database server, even when using the middle-tier. This will return an empty string if Dns lookup fails.</summary> public static string GetODServer() { if (RemotingClient.RemotingRole == RemotingRole.ClientWeb) { return(Meth.GetString(MethodBase.GetCurrentMethod())); } //string command="SELECT @@hostname";//This command fails in MySQL 5.0.22 (the version of MySQL 5.0 we used to use), because the hostname variable was added in MySQL 5.0.38. //string rawHostName=DataConnection.GetServerName();//This could be a human readable name, or it might be "localhost" or "127.0.0.1" or another IP address. //return Dns.GetHostEntry(rawHostName).HostName;//Return the human readable name (full domain name) corresponding to the rawHostName. //Had to strip off the port, caused Dns.GetHostEntry to fail and is not needed to get the hostname string rawHostName = DataConnection.GetServerName(); if (rawHostName != null) //rawHostName will be null if the user used a custom ConnectionString when they chose their database. { rawHostName = rawHostName.Split(':')[0]; //This could be a human readable name, or it might be "localhost" or "127.0.0.1" or another IP address. } string retval = ""; try { retval = Dns.GetHostEntry(rawHostName).HostName; //Return the human readable name (full domain name) corresponding to the rawHostName. } catch (Exception ex) { ex.DoNothing(); } return(retval); }
///<summary>Pass in a serialized dto. It returns a dto which must be deserialized by the client. ///Set serverMapPath to the root directory of the OpenDentalServerConfig.xml. Typically Server.MapPath(".") from a web service. ///Optional parameter because it is not necessary for Unit Tests (mock server).</summary> public static string ProcessDto(string dtoString, string serverMapPath = "") { try { #region Normalize DateTime if (!_isMiddleTierInitialized) { //If this fails, the exception will throw and be serialized and sent to the client. ODInitialize.Initialize(); //Because Security._curUserT is a thread static field, we need to make sure that any new threads that are spawned have that field set. ODThread.AddInitializeHandler <Userod>(() => Security.CurUser.Copy(), user => Security.CurUser = user); //Same thing for PasswordTyped. ODThread.AddInitializeHandler <string>(() => Security.PasswordTyped, password => Security.PasswordTyped = password); //Ditto for CurComputerName ODThread.AddInitializeHandler <string>(() => Security.CurComputerName, computerName => Security.CurComputerName = computerName); //Calling CDT.Class1.Decrypt will cause CDT to verify the assembly and then save the encryption key. This needs to be done here because //if we later call CDT.Class1.Decrypt inside a thread, we won't we able to find OpenDentalServer.dll in the call stack. ODException.SwallowAnyException(() => CDT.Class1.Decrypt("odv2e$fakeciphertext", out _)); _isMiddleTierInitialized = true; } #endregion #region Initialize Database Connection //Always attempt to set the database connection settings from the config file if they haven't been set yet. //We use to ONLY load in database settings when Security.LogInWeb was called but that is not good enough now that we have more services. //E.g. We do not want to manually call "Security.LogInWeb" from the CEMT when all we want is a single preference value. if (string.IsNullOrEmpty(DataConnection.GetServerName()) && string.IsNullOrEmpty(DataConnection.GetConnectionString())) { RemotingClient.RemotingRole = RemotingRole.ServerWeb; //the application virtual path is usually /OpenDentalServer, but may be different if hosting multiple databases on one IIS server string configFilePath = ""; if (!string.IsNullOrWhiteSpace(HostingEnvironment.ApplicationVirtualPath) && HostingEnvironment.ApplicationVirtualPath.Length > 1) { //There can be multiple config files within a physical path that is shared by multiple IIS ASP.NET applications. //In order for the same physical path to host multiple applications, they each need a unique config file for db connection settings. //Each application will have a unique ApplicationVirtualPath which we will use to identify the corresponding config.xml. configFilePath = ODFileUtils.CombinePaths(serverMapPath, HostingEnvironment.ApplicationVirtualPath.Trim('/') + "Config.xml"); } if (string.IsNullOrWhiteSpace(configFilePath) || !File.Exists(configFilePath)) //returns false if the file doesn't exist, user doesn't have permission for file, path is blank or null { //either configFilePath not set or file doesn't exist, default to OpenDentalServerConfig.xml configFilePath = ODFileUtils.CombinePaths(serverMapPath, "OpenDentalServerConfig.xml"); } Userods.LoadDatabaseInfoFromFile(configFilePath); } #endregion DataTransferObject dto = DataTransferObject.Deserialize(dtoString); DtoInformation dtoInformation = new DtoInformation(dto); if (dtoInformation.FullNameComponents.Length == 3 && dtoInformation.FullNameComponents[2].ToLower() == "hashpassword") { return(dtoInformation.GetHashPassword()); } //Set Security.CurUser so that queries can be run against the db as if it were this user. Security.CurUser = Userods.CheckUserAndPassword(dto.Credentials.Username, dto.Credentials.Password , Programs.UsingEcwTightOrFullMode()); Security.PasswordTyped = dto.Credentials.Password; //Set the computer name so securitylog entries use the client's computer name instead of the middle tier server name //Older clients might not include ComputerName in the dto, so we need to make sure it's not null. Security.CurComputerName = dto.ComputerName ?? ""; Type type = dto.GetType(); #region DtoGetTable if (type == typeof(DtoGetTable)) { DataTable dt = (DataTable)dtoInformation.MethodInfo.Invoke(null, dtoInformation.ParamObjs); return(XmlConverter.TableToXml(dt)); } #endregion #region DtoGetTableLow else if (type == typeof(DtoGetTableLow)) { DataTable dt = Reports.GetTable((string)dtoInformation.ParamObjs[0]); return(XmlConverter.TableToXml(dt)); } #endregion #region DtoGetDS else if (type == typeof(DtoGetDS)) { DataSet ds = (DataSet)dtoInformation.MethodInfo.Invoke(null, dtoInformation.ParamObjs); return(XmlConverter.DsToXml(ds)); } #endregion #region DtoGetSerializableDictionary else if (type == typeof(DtoGetSerializableDictionary)) { Object objResult = dtoInformation.MethodInfo.Invoke(null, dtoInformation.ParamObjs); Type returnType = dtoInformation.MethodInfo.ReturnType; return(XmlConverterSerializer.Serialize(returnType, objResult)); } #endregion #region DtoGetLong else if (type == typeof(DtoGetLong)) { long longResult = (long)dtoInformation.MethodInfo.Invoke(null, dtoInformation.ParamObjs); return(longResult.ToString()); } #endregion #region DtoGetInt else if (type == typeof(DtoGetInt)) { int intResult = (int)dtoInformation.MethodInfo.Invoke(null, dtoInformation.ParamObjs); return(intResult.ToString()); } #endregion #region DtoGetDouble else if (type == typeof(DtoGetDouble)) { double doubleResult = (double)dtoInformation.MethodInfo.Invoke(null, dtoInformation.ParamObjs); return(doubleResult.ToString()); } #endregion #region DtoGetVoid else if (type == typeof(DtoGetVoid)) { dtoInformation.MethodInfo.Invoke(null, dtoInformation.ParamObjs); return("0"); } #endregion #region DtoGetObject else if (type == typeof(DtoGetObject)) { if (dtoInformation.ClassName == "Security" && dtoInformation.MethodName == "LogInWeb") { dtoInformation.Parameters[2] = new DtoObject(serverMapPath, typeof(string)); //because we can't access this variable from within OpenDentBusiness. RemotingClient.RemotingRole = RemotingRole.ServerWeb; //We just changed the number of parameters so we need to regenerate ParamObjs. dtoInformation.ParamObjs = DtoObject.GenerateObjects(dtoInformation.Parameters); } Object objResult = dtoInformation.MethodInfo.Invoke(null, dtoInformation.ParamObjs); Type returnType = dtoInformation.MethodInfo.ReturnType; if (returnType.IsInterface) { objResult = new DtoObject(objResult, objResult?.GetType() ?? returnType); returnType = typeof(DtoObject); } return(XmlConverterSerializer.Serialize(returnType, objResult)); } #endregion #region DtoGetString else if (type == typeof(DtoGetString)) { string strResult = (string)dtoInformation.MethodInfo.Invoke(null, dtoInformation.ParamObjs); return(XmlConverter.XmlEscape(strResult)); } #endregion #region DtoGetBool else if (type == typeof(DtoGetBool)) { bool boolResult = (bool)dtoInformation.MethodInfo.Invoke(null, dtoInformation.ParamObjs); return(boolResult.ToString()); } #endregion else { throw new NotSupportedException("Dto type not supported: " + type.FullName); } } catch (Exception e) { DtoException exception = new DtoException(); if (e.InnerException == null) { exception = GetDtoException(e); } else { exception = GetDtoException(e.InnerException); } return(exception.Serialize()); } }
///<summary>Backs up the database to the same directory as the original just in case the user did not have sense enough to do a backup first. ///Does not work for Oracle, due to some MySQL specific commands inside.</summary> public static void MakeABackup(string serverName = "", string user = "", string pass = "", bool doVerify = false) { //This function should always make the backup on the server itself, and since no directories are //referred to (all handled with MySQL), this function will always be referred to the server from //client machines. if (RemotingClient.RemotingRole == RemotingRole.ClientWeb) { Meth.GetVoid(MethodBase.GetCurrentMethod(), serverName, user, pass, doVerify); return; } //UpdateStreamLinePassword is purposefully named poorly and used in an odd fashion to sort of obfuscate it from our users. //GetStringNoCache() will return blank if pref does not exist. if (PrefC.GetStringNoCache(PrefName.UpdateStreamLinePassword) == "abracadabra") { return; } string currentServerName = DataConnection.GetServerName().ToLower(); bool useSameServer = string.IsNullOrWhiteSpace(serverName) || currentServerName.Equals(serverName, StringComparison.CurrentCultureIgnoreCase); if (!string.IsNullOrWhiteSpace(serverName) && currentServerName == "localhost" && serverName.ToLower() != "localhost") //there could be a mismatch but technically the same server { useSameServer = serverName.Equals(Environment.MachineName, StringComparison.CurrentCultureIgnoreCase); } if (serverName.ToLower() == "localhost" && currentServerName != "localhost") //there could be a mismatch but technically the same server { useSameServer = currentServerName.Equals(Environment.MachineName, StringComparison.CurrentCultureIgnoreCase); } //only used in two places: upgrading version, and upgrading mysql version. //Both places check first to make sure user is using mysql. //we have to be careful to throw an exception if the backup is failing. DataConnection dcon = new DataConnection(); //if they provided a different server where they want their backup to be, we need a separate connection for that DataConnection dconBackupServer = useSameServer ? new DataConnection() : new DataConnection(serverName, "", user, pass, DatabaseType.MySql); //Check that the backup server does not already contain this database string command = "SELECT database()"; DataTable table = dcon.GetTable(command); string oldDb = PIn.String(table.Rows[0][0].ToString()); string newDb = oldDb + "backup_" + DateTime.Today.ToString("MM_dd_yyyy"); command = "SHOW DATABASES"; table = dconBackupServer.GetTable(command); string[] databases = new string[table.Rows.Count]; for (int i = 0; i < table.Rows.Count; i++) { databases[i] = table.Rows[i][0].ToString(); } if (Contains(databases, newDb)) //if the new database name already exists //find a unique one { int uniqueID = 1; string originalNewDb = newDb; do { newDb = originalNewDb + "_" + uniqueID.ToString(); uniqueID++; }while(Contains(databases, newDb)); } command = "CREATE DATABASE `" + newDb + "` CHARACTER SET utf8"; dconBackupServer.NonQ(command); //create the backup db on the backup server //But get the tables from the current, not the backup server command = "SHOW FULL TABLES WHERE Table_type='BASE TABLE'"; //Tables, not views. Does not work in MySQL 4.1, however we test for MySQL version >= 5.0 in PrefL. table = dcon.GetTable(command); //Set the connection to the new database now that it has been created dconBackupServer = useSameServer?new DataConnection(newDb):new DataConnection(serverName, newDb, user, pass, DatabaseType.MySql); foreach (DataRow row in table.Rows) { string tableName = row[0].ToString(); //First create the table on the new db MiscDataEvent.Fire(ODEventType.MiscData, $"Backing up table: {tableName}"); //also works with views. Added backticks around table name for unusual characters. command = $"SHOW CREATE TABLE `{oldDb}`.`{tableName}`"; DataTable dtCreate = dcon.GetTable(command); command = PIn.ByteArray(dtCreate.Rows[0][1]); //The backup database tables will be MyISAM because it is significantly faster at doing bulk inserts. command = command.Replace("ENGINE=InnoDB", "ENGINE=MyISAM"); dconBackupServer.NonQ(command); //Then copy the data into the new table if (useSameServer) { //If on the same server we can select into directly, which is faster command = $"INSERT INTO `{newDb}`.`{tableName}` SELECT * FROM `{oldDb}`.`{tableName}`"; //Added backticks around table name for unusual characters. dconBackupServer.NonQ(command); } else { long count = PIn.Long(dcon.GetCount($"SELECT COUNT(*) FROM `{oldDb}`.`{tableName}`")); int limit = 10000; if (tableName == "documentmisc") //This table can have really large rows so just to be safe, handle the backup one row at a time { limit = 1; } int offset = 0; while (count > offset) { DataTable dtOld = dcon.GetTable($" SELECT * FROM `{oldDb}`.`{tableName}` LIMIT {limit} OFFSET {offset}"); offset += dtOld.Rows.Count; dconBackupServer.BulkCopy(dtOld, tableName); } } } //Verify that the old database and the new backup have the same number of rows if (doVerify) { List <string> listTablesFailed = new List <string>(); foreach (DataRow dbTable in table.Rows) { string tableName = dbTable[0].ToString(); MiscDataEvent.Fire(ODEventType.MiscData, $"Verifying backup: {tableName}"); int ctOld = PIn.Int(dcon.GetCount($"SELECT COUNT(*) FROM `{oldDb}`.`{tableName}`")); int ctNew = PIn.Int(dconBackupServer.GetCount($"SELECT COUNT(*) FROM `{newDb}`.`{tableName}`")); if (ctOld != ctNew) { listTablesFailed.Add(tableName); } } if (listTablesFailed.Count > 0) { throw new Exception($@"Failed to create database backup because the following tables contained a different number of rows than expected: {string.Join(", ",listTablesFailed)}." ); } } }
///<summary>Pass in a serialized dto. It returns a dto which must be deserialized by the client. ///Set serverMapPath to the root directory of the OpenDentalServerConfig.xml. Typically Server.MapPath(".") from a web service. ///Optional parameter because it is not necessary for Unit Tests (mock server).</summary> public static string ProcessDto(string dtoString, string serverMapPath = "") { #if DEBUG //System.Threading.Thread.Sleep(100);//to test slowness issues with web service. #endif DataTransferObject dto = DataTransferObject.Deserialize(dtoString); try { string[] methNameComps = GetComponentsFromDtoMeth(dto.MethodName); if (methNameComps.Length == 3 && methNameComps[2].ToLower() == "hashpassword") { return(GetHashPassword(dto)); } //Always attempt to set the database connection settings from the config file if they haven't been set yet. //We use to ONLY load in database settings when Security.LogInWeb was called but that is not good enough now that we have more services. //E.g. We do not want to manually call "Security.LogInWeb" from the CEMT when all we want is a single preference value. if (string.IsNullOrEmpty(DataConnection.GetServerName()) && string.IsNullOrEmpty(DataConnection.GetConnectionString())) { RemotingClient.RemotingRole = RemotingRole.ServerWeb; //the application virtual path is usually /OpenDentalServer, but may be different if hosting multiple databases on one IIS server string configFilePath = ""; if (!string.IsNullOrWhiteSpace(HostingEnvironment.ApplicationVirtualPath) && HostingEnvironment.ApplicationVirtualPath.Length > 1) { //There can be multiple config files within a physical path that is shared by multiple IIS ASP.NET applications. //In order for the same physical path to host multiple applications, they each need a unique config file for db connection settings. //Each application will have a unique ApplicationVirtualPath which we will use to identify the corresponding config.xml. configFilePath = ODFileUtils.CombinePaths(serverMapPath, HostingEnvironment.ApplicationVirtualPath.Trim('/') + "Config.xml"); } if (string.IsNullOrWhiteSpace(configFilePath) || !File.Exists(configFilePath)) //returns false if the file doesn't exist, user doesn't have permission for file, path is blank or null { //either configFilePath not set or file doesn't exist, default to OpenDentalServerConfig.xml configFilePath = ODFileUtils.CombinePaths(serverMapPath, "OpenDentalServerConfig.xml"); } Userods.LoadDatabaseInfoFromFile(configFilePath); } //Set Security.CurUser so that queries can be run against the db as if it were this user. Security.CurUser = Userods.CheckUserAndPassword(dto.Credentials.Username , dto.Credentials.Password , Programs.IsEnabled(ProgramName.eClinicalWorks)); Security.PasswordTyped = dto.Credentials.Password; Type type = dto.GetType(); #region DtoGetTable if (type == typeof(DtoGetTable)) { DtoGetTable dtoGetTable = (DtoGetTable)dto; string[] fullNameComponents = GetComponentsFromDtoMeth(dtoGetTable.MethodName); string assemblyName = fullNameComponents[0]; //OpenDentBusiness or else a plugin name string className = fullNameComponents[1]; string methodName = fullNameComponents[2]; Type classType = null; Assembly ass = Plugins.GetAssembly(assemblyName); if (ass == null) { classType = Type.GetType(assemblyName //actually, the namespace which we require to be same as assembly by convention + "." + className + "," + assemblyName); } else //plugin was found { classType = ass.GetType(assemblyName //actually, the namespace which we require to be same as assembly by convention + "." + className); } DtoObject[] parameters = dtoGetTable.Params; Type[] paramTypes = DtoObject.GenerateTypes(parameters, assemblyName); MethodInfo methodInfo = classType.GetMethod(methodName, paramTypes); if (methodInfo == null) { throw new ApplicationException("Method not found with " + parameters.Length.ToString() + " parameters: " + dtoGetTable.MethodName); } object[] paramObjs = DtoObject.GenerateObjects(parameters); DataTable dt = (DataTable)methodInfo.Invoke(null, paramObjs); String response = XmlConverter.TableToXml(dt); return(response); } #endregion #region DtoGetTableLow else if (type == typeof(DtoGetTableLow)) { DtoGetTableLow dtoGetTableLow = (DtoGetTableLow)dto; DtoObject[] parameters = dtoGetTableLow.Params; object[] paramObjs = DtoObject.GenerateObjects(parameters); DataTable dt = Reports.GetTable((string)paramObjs[0]); String response = XmlConverter.TableToXml(dt); return(response); } #endregion #region DtoGetDS else if (type == typeof(DtoGetDS)) { DtoGetDS dtoGetDS = (DtoGetDS)dto; string[] fullNameComponents = GetComponentsFromDtoMeth(dtoGetDS.MethodName); string assemblyName = fullNameComponents[0]; //OpenDentBusiness or else a plugin name string className = fullNameComponents[1]; string methodName = fullNameComponents[2]; Type classType = null; Assembly ass = Plugins.GetAssembly(assemblyName); if (ass == null) { classType = Type.GetType(assemblyName //actually, the namespace which we require to be same as assembly by convention + "." + className + "," + assemblyName); } else //plugin was found { classType = ass.GetType(assemblyName //actually, the namespace which we require to be same as assembly by convention + "." + className); } DtoObject[] parameters = dtoGetDS.Params; Type[] paramTypes = DtoObject.GenerateTypes(parameters, assemblyName); MethodInfo methodInfo = classType.GetMethod(methodName, paramTypes); if (methodInfo == null) { throw new ApplicationException("Method not found with " + parameters.Length.ToString() + " parameters: " + dtoGetDS.MethodName); } object[] paramObjs = DtoObject.GenerateObjects(parameters); DataSet ds = (DataSet)methodInfo.Invoke(null, paramObjs); String response = XmlConverter.DsToXml(ds); return(response); } #endregion #region DtoGetSerializableDictionary else if (type == typeof(DtoGetSerializableDictionary)) { DtoGetSerializableDictionary dtoGetSD = (DtoGetSerializableDictionary)dto; string[] fullNameComponents = GetComponentsFromDtoMeth(dtoGetSD.MethodName); string assemblyName = fullNameComponents[0]; //OpenDentBusiness or else a plugin name string className = fullNameComponents[1]; string methodName = fullNameComponents[2]; Type classType = null; Assembly ass = Plugins.GetAssembly(assemblyName); if (ass == null) { classType = Type.GetType(assemblyName //actually, the namespace which we require to be same as assembly by convention + "." + className + "," + assemblyName); } else //plugin was found { classType = ass.GetType(assemblyName //actually, the namespace which we require to be same as assembly by convention + "." + className); } DtoObject[] parameters = dtoGetSD.Params; Type[] paramTypes = DtoObject.GenerateTypes(parameters, assemblyName); MethodInfo methodInfo = classType.GetMethod(methodName, paramTypes); if (methodInfo == null) { throw new ApplicationException("Method not found with " + parameters.Length.ToString() + " parameters: " + dtoGetSD.MethodName); } object[] paramObjs = DtoObject.GenerateObjects(parameters); Object objResult = methodInfo.Invoke(null, paramObjs); Type returnType = methodInfo.ReturnType; return(XmlConverter.Serialize(returnType, objResult)); } #endregion #region DtoGetLong else if (type == typeof(DtoGetLong)) { DtoGetLong dtoGetLong = (DtoGetLong)dto; string[] fullNameComponents = GetComponentsFromDtoMeth(dtoGetLong.MethodName); string assemblyName = fullNameComponents[0]; //OpenDentBusiness or else a plugin name string className = fullNameComponents[1]; string methodName = fullNameComponents[2]; Type classType = null; Assembly ass = Plugins.GetAssembly(assemblyName); if (ass == null) { classType = Type.GetType(assemblyName //actually, the namespace which we require to be same as assembly by convention + "." + className + "," + assemblyName); } else //plugin was found { classType = ass.GetType(assemblyName //actually, the namespace which we require to be same as assembly by convention + "." + className); } DtoObject[] parameters = dtoGetLong.Params; Type[] paramTypes = DtoObject.GenerateTypes(parameters, assemblyName); MethodInfo methodInfo = classType.GetMethod(methodName, paramTypes); if (methodInfo == null) { throw new ApplicationException("Method not found with " + parameters.Length.ToString() + " parameters: " + dtoGetLong.MethodName); } object[] paramObjs = DtoObject.GenerateObjects(parameters); long longResult = (long)methodInfo.Invoke(null, paramObjs); return(longResult.ToString()); } #endregion #region DtoGetInt else if (type == typeof(DtoGetInt)) { DtoGetInt dtoGetInt = (DtoGetInt)dto; string[] fullNameComponents = GetComponentsFromDtoMeth(dtoGetInt.MethodName); string assemblyName = fullNameComponents[0]; //OpenDentBusiness or else a plugin name string className = fullNameComponents[1]; string methodName = fullNameComponents[2]; Type classType = null; Assembly ass = Plugins.GetAssembly(assemblyName); if (ass == null) { classType = Type.GetType(assemblyName //actually, the namespace which we require to be same as assembly by convention + "." + className + "," + assemblyName); } else //plugin was found { classType = ass.GetType(assemblyName //actually, the namespace which we require to be same as assembly by convention + "." + className); } DtoObject[] parameters = dtoGetInt.Params; Type[] paramTypes = DtoObject.GenerateTypes(parameters, assemblyName); MethodInfo methodInfo = classType.GetMethod(methodName, paramTypes); if (methodInfo == null) { throw new ApplicationException("Method not found with " + parameters.Length.ToString() + " parameters: " + dtoGetInt.MethodName); } object[] paramObjs = DtoObject.GenerateObjects(parameters); int intResult = (int)methodInfo.Invoke(null, paramObjs); return(intResult.ToString()); } #endregion #region DtoGetDouble else if (type == typeof(DtoGetDouble)) { DtoGetDouble dtoGetDouble = (DtoGetDouble)dto; string[] fullNameComponents = GetComponentsFromDtoMeth(dtoGetDouble.MethodName); string assemblyName = fullNameComponents[0]; //OpenDentBusiness or else a plugin name string className = fullNameComponents[1]; string methodName = fullNameComponents[2]; Type classType = null; Assembly ass = Plugins.GetAssembly(assemblyName); if (ass == null) { classType = Type.GetType(assemblyName //actually, the namespace which we require to be same as assembly by convention + "." + className + "," + assemblyName); } else //plugin was found { classType = ass.GetType(assemblyName //actually, the namespace which we require to be same as assembly by convention + "." + className); } DtoObject[] parameters = dtoGetDouble.Params; Type[] paramTypes = DtoObject.GenerateTypes(parameters, assemblyName); MethodInfo methodInfo = classType.GetMethod(methodName, paramTypes); if (methodInfo == null) { throw new ApplicationException("Method not found with " + parameters.Length.ToString() + " parameters: " + dtoGetDouble.MethodName); } object[] paramObjs = DtoObject.GenerateObjects(parameters); double doubleResult = (double)methodInfo.Invoke(null, paramObjs); return(doubleResult.ToString()); } #endregion #region DtoGetVoid else if (type == typeof(DtoGetVoid)) { DtoGetVoid dtoGetVoid = (DtoGetVoid)dto; string[] fullNameComponents = GetComponentsFromDtoMeth(dtoGetVoid.MethodName); string assemblyName = fullNameComponents[0]; //OpenDentBusiness or else a plugin name string className = fullNameComponents[1]; string methodName = fullNameComponents[2]; Type classType = null; Assembly ass = Plugins.GetAssembly(assemblyName); if (ass == null) { classType = Type.GetType(assemblyName //actually, the namespace which we require to be same as assembly by convention + "." + className + "," + assemblyName); } else //plugin was found { classType = ass.GetType(assemblyName //actually, the namespace which we require to be same as assembly by convention + "." + className); } DtoObject[] parameters = dtoGetVoid.Params; Type[] paramTypes = DtoObject.GenerateTypes(parameters, assemblyName); MethodInfo methodInfo = classType.GetMethod(methodName, paramTypes); if (methodInfo == null) { throw new ApplicationException("Method not found with " + parameters.Length.ToString() + " parameters: " + dtoGetVoid.MethodName); } object[] paramObjs = DtoObject.GenerateObjects(parameters); methodInfo.Invoke(null, paramObjs); return("0"); } #endregion #region DtoGetObject else if (type == typeof(DtoGetObject)) { DtoGetObject dtoGetObject = (DtoGetObject)dto; string[] fullNameComponents = GetComponentsFromDtoMeth(dtoGetObject.MethodName); string assemblyName = fullNameComponents[0]; //OpenDentBusiness or else a plugin name string className = fullNameComponents[1]; string methodName = fullNameComponents[2]; //if(className != "Security" || methodName != "LogInWeb") {//because credentials will be checked inside that method // Userods.CheckCredentials(dtoGetObject.Credentials);//will throw exception if fails. //} Type classType = null; Assembly ass = Plugins.GetAssembly(assemblyName); //if(className!="Security" || methodName!="LogInWeb") {//Do this for everything except Security.LogInWeb, because Plugins.GetAssembly will fail in that case. // ass=Plugins.GetAssembly(assemblyName); //} if (ass == null) { classType = Type.GetType(assemblyName //actually, the namespace which we require to be same as assembly by convention + "." + className + "," + assemblyName); } else //plugin was found { classType = ass.GetType(assemblyName //actually, the namespace which we require to be same as assembly by convention + "." + className); } DtoObject[] parameters = dtoGetObject.Params; Type[] paramTypes = DtoObject.GenerateTypes(parameters, assemblyName); MethodInfo methodInfo = classType.GetMethod(methodName, paramTypes); if (methodInfo == null) { throw new ApplicationException("Method not found with " + parameters.Length.ToString() + " parameters: " + dtoGetObject.MethodName); } if (className == "Security" && methodName == "LogInWeb") { parameters[2] = new DtoObject(serverMapPath, typeof(string)); //because we can't access this variable from within OpenDentBusiness. RemotingClient.RemotingRole = RemotingRole.ServerWeb; } object[] paramObjs = DtoObject.GenerateObjects(parameters); Object objResult = methodInfo.Invoke(null, paramObjs); Type returnType = methodInfo.ReturnType; if (returnType.IsInterface) { objResult = new DtoObject(objResult, objResult?.GetType() ?? returnType); returnType = typeof(DtoObject); } return(XmlConverter.Serialize(returnType, objResult)); } #endregion #region DtoGetString else if (type == typeof(DtoGetString)) { DtoGetString dtoGetString = (DtoGetString)dto; string[] fullNameComponents = GetComponentsFromDtoMeth(dtoGetString.MethodName); string assemblyName = fullNameComponents[0]; //OpenDentBusiness or else a plugin name string className = fullNameComponents[1]; string methodName = fullNameComponents[2]; Type classType = null; Assembly ass = Plugins.GetAssembly(assemblyName); if (ass == null) { classType = Type.GetType(assemblyName //actually, the namespace which we require to be same as assembly by convention + "." + className + "," + assemblyName); } else //plugin was found { classType = ass.GetType(assemblyName //actually, the namespace which we require to be same as assembly by convention + "." + className); } DtoObject[] parameters = dtoGetString.Params; Type[] paramTypes = DtoObject.GenerateTypes(parameters, assemblyName); MethodInfo methodInfo = classType.GetMethod(methodName, paramTypes); if (methodInfo == null) { throw new ApplicationException("Method not found with " + parameters.Length.ToString() + " parameters: " + dtoGetString.MethodName); } object[] paramObjs = DtoObject.GenerateObjects(parameters); string strResult = (string)methodInfo.Invoke(null, paramObjs); strResult = XmlConverter.XmlEscape(strResult); return(strResult); } #endregion #region DtoGetBool else if (type == typeof(DtoGetBool)) { DtoGetBool dtoGetBool = (DtoGetBool)dto; string[] fullNameComponents = GetComponentsFromDtoMeth(dtoGetBool.MethodName); string assemblyName = fullNameComponents[0]; //OpenDentBusiness or else a plugin name string className = fullNameComponents[1]; string methodName = fullNameComponents[2]; Type classType = null; Assembly ass = Plugins.GetAssembly(assemblyName); if (ass == null) { classType = Type.GetType(assemblyName //actually, the namespace which we require to be same as assembly by convention + "." + className + "," + assemblyName); } else //plugin was found { classType = ass.GetType(assemblyName //actually, the namespace which we require to be same as assembly by convention + "." + className); } DtoObject[] parameters = dtoGetBool.Params; Type[] paramTypes = DtoObject.GenerateTypes(parameters, assemblyName); MethodInfo methodInfo = classType.GetMethod(methodName, paramTypes); if (methodInfo == null) { throw new ApplicationException("Method not found with " + parameters.Length.ToString() + " parameters: " + dtoGetBool.MethodName); } object[] paramObjs = DtoObject.GenerateObjects(parameters); bool boolResult = (bool)methodInfo.Invoke(null, paramObjs); return(boolResult.ToString()); } #endregion else { throw new NotSupportedException("Dto type not supported: " + type.FullName); } } catch (Exception e) { DtoException exception = new DtoException(); exception.ExceptionType = e.GetType().BaseType.Name; //Since the exception was down converted to a regular exception, we need the BaseType. if (e.InnerException == null) { exception.Message = e.Message; } else { exception.Message = e.InnerException.Message; } return(exception.Serialize()); } }