context: using System;
  using System.Collections.Generic;
  using System.Data;
  using System.IO;
  using System.Reflection;
  using System.Security;
  using System.Security.Cryptography;
  using System.Text;
  using System.Xml;
  using System.Xml.XPath;
  using System.Web;
  using CodeBase;
  using System.Windows.Forms;
  
  namespace OpenDentBusiness {
  	///<summary>(Users OD)</summary>
  	public class Userods {
  		//private static bool webServerConfigHasLoadedd=false;
  		
  		///<summary></summary>
  		public static DataTable RefreshCache() {
  			//No need to check RemotingRole; Calls GetTableRemotelyIfNeeded().
  			string command="SELECT * FROM userod ORDER BY UserName";
  			DataTable table=Cache.GetTableRemotelyIfNeeded(MethodBase.GetCurrentMethod(),command);
  			table.TableName="Userod";
  			FillCache(table);
  			return table;
  		}
  
  		public static void FillCache(DataTable table){
  			//No need to check RemotingRole; no call to db.
  			List<Userod> listUserodsLong=Crud.UserodCrud.TableToList(table);
  			List<Userod> listUserodsShort=new List<Userod>();
  			for(int i=0;i<listUserodsLong.Count;i++) {//This logic used to be in UserodC, but didn't really follow our new pattern.
  				if(listUserodsLong[i].IsHidden || listUserodsLong[i].UserNumCEMT!=0) {
  					continue;
  				}
  				listUserodsShort.Add(listUserodsLong[i]);
  			}
  			UserodC.Listt=listUserodsLong;
  			UserodC.ShortList=listUserodsShort;
  		}
  
  		///<summary></summary>
  		public static Userod GetUser(long userNum) {
  			//No need to check RemotingRole; no call to db.
  			List<Userod> listUserods=UserodC.GetListt();
  			if(listUserods==null){
  				RefreshCache();
  			}
  			for(int i=0;i<listUserods.Count;i++) {
  				if(listUserods[i].UserNum==userNum){
  					return listUserods[i];
  				}
  			}
  			return null;
  		}
  
  		///<summary>Returns a list of all non-hidden users.  Does not include CEMT users.</summary>
  		public static List<Userod> GetUsers() {
  			return GetUsers(false);
  		}
  
  		///<summary>Returns a list of all non-hidden users.  Set includeCEMT to true if you want CEMT users included.</summary>
  		public static List<Userod> GetUsers(bool includeCEMT) {
  			List<Userod> retVal=new List<Userod>();
  			List<Userod> listUsersLong=UserodC.GetListt();
  			for(int i=0;i<listUsersLong.Count;i++) {
  				if(listUsersLong[i].IsHidden) {
  					continue;
  				}
  				if(!includeCEMT && listUsersLong[i].UserNumCEMT!=0) {
  					continue;
  				}
  				retVal.Add(listUsersLong[i]);
  			}
  			return retVal;
  		}
  
  		///<summary>Returns a list of all non-hidden CEMT users.</summary>
  		public static List<Userod> GetUsersForCEMT() {
  			List<Userod> retVal=new List<Userod>();
  			List<Userod> listUsers=UserodC.GetListt();
  			for(int i=0;i<listUsers.Count;i++) {
  				if(listUsers[i].UserNumCEMT==0) {
  					continue;
  				}
  				retVal.Add(listUsers[i]);
  			}
  			return retVal;
  		}
  
  		///<summary>Returns null if not found.  isEcwTight is not case sensitive.</summary>
  		public static Userod GetUserByName(string userName,bool isEcwTight) {
  			//No need to check RemotingRole; no call to db.
  			List<Userod> listUserods=UserodC.GetListt();
  			if(listUserods==null) {
  				RefreshCache();
  			}
  			for(int i=0;i<listUserods.Count;i++) {
  				if(listUserods[i].IsHidden){
  					continue;
  				}
  				if(isEcwTight) {
  					if(listUserods[i].UserName.ToLower()==userName.ToLower()) {
  						return listUserods[i];
  					}
  				}
  				else {
  					if(listUserods[i].UserName==userName) {//exact
  						return listUserods[i];
  					}
  				}
  			}
  			return null;
  		}
  
  		///<summary>Returns null if not found.</summary>
  		public static Userod GetUserByEmployeeNum(long employeeNum) {
  			//No need to check RemotingRole; no call to db.
  			List<Userod> listUserods=UserodC.GetListt();
  			for(int i=0;i<listUserods.Count;i++) {
  				if(listUserods[i].EmployeeNum==employeeNum) {
  					return listUserods[i];
  				}
  			}
  			return null;
  		}
  
  		///<summary>Returns all users that are associated to the employee passed in.  Returns empty list if no matches found.</summary>
  		public static List<Userod> GetUsersByEmployeeNum(long employeeNum) {
  			//No need to check RemotingRole; no call to db.
  			List<Userod> listUserodsLong=UserodC.GetListt();
  			List<Userod> listUserods=new List<Userod>();
  			for(int i=0;i<listUserodsLong.Count;i++) {
  				if(listUserodsLong[i].EmployeeNum==employeeNum) {
  					listUserods.Add(listUserodsLong[i]);
  				}
  			}
  			return listUserods;
  		}
  
  		///<summary>This handles situations where we have a usernum, but not a user.  And it handles usernum of zero.</summary>
  		public static string GetName(long userNum){
  			//No need to check RemotingRole; no call to db.
  			if(userNum==0){
  				return "";
  			}
  			Userod user=GetUser(userNum);
  			if(user==null){
  				return "";
  			}
  			return user.UserName;
  		}
  
  
  		///<summary>Only used in one place on the server when first attempting to log on.  The password will be hashed and checked against the one in the database.  Password is required, so empty string will return null.  Returns a user object if user and password are valid.  Otherwise, returns null.  If usingEcw, password will actually be the hash.  If usingEcw, then the username is not case sensitive.</summary>
  		public static Userod CheckUserAndPassword(string username,string password,bool usingEcw) {
  			//No need to check RemotingRole; no call to db.
  			if(password==""){
  				return null;
  			}
  			RefreshCache();
  			Userod user=GetUserByName(username,usingEcw);
  			if(user==null){
  				return null;
  			}
  			if(usingEcw){
  				if(user.Password==password) {
  					return user;
  				}
  			}
  			else if(user.Password==EncryptPassword(password)) {
  				return user;
  			}
  			return null;
  		}
  
  		///<summary>Used by Server.  Throws exception if bad username or passhash or if either are blank.  It uses cached user list, refreshing it if null.  This is used everywhere except in the log on screen.</summary>
  		public static void CheckCredentials(Credentials cred){
  			//No need to check RemotingRole; no call to db.
  			#if DEBUG
  				return;//skip checking credentials when in debug for faster testing.
  			#endif
  			if(cred.Username=="" || cred.Password==""){
  				throw new ApplicationException("Invalid username or password.");
  			}
  			Userod userod=null;
  			List<Userod> listUserods=UserodC.GetListt();
  			for(int i=0;i<listUserods.Count;i++){
  				if(listUserods[i].UserName==cred.Username){
  					userod=listUserods[i];
  					break;
  				}
  			}
  			if(userod==null){
  				throw new ApplicationException("Invalid username or password.");
  			}
  			bool useEcwAlgorithm=Programs.IsEnabled(ProgramName.eClinicalWorks);
  			if(useEcwAlgorithm){
  				if(userod.Password!=cred.Password){
  					throw new ApplicationException("Invalid username or password.");
  				}
  			}
  			else if(userod.Password!=EncryptPassword(cred.Password)){
  				throw new ApplicationException("Invalid username or password.");
  			}
  		}
  
  		///<summary>Will throw an exception if it fails for any reason.  This will directly access the config file on the disk, read the values, and set the DataConnection to the new database.  This is only triggered when someone tries to log on.</summary>
  		public static void LoadDatabaseInfoFromFile(string configFilePath){
  			//No need to check RemotingRole; no call to db.
  			if(!File.Exists(configFilePath)){
  				throw new Exception("Could not find "+configFilePath+" on the web server.");
  			}
  			XmlDocument doc=new XmlDocument();
  			try {
  				doc.Load(configFilePath);
  			}
  			catch{
  				throw new Exception("Web server "+configFilePath+" could not be opened or is in an invalid format.");
  			}
  			XPathNavigator Navigator=doc.CreateNavigator();
  			//always picks the first database entry in the file:
  			XPathNavigator navConn=Navigator.SelectSingleNode("//DatabaseConnection");//[Database='"+database+"']");
  			if(navConn==null) {
  				throw new Exception(configFilePath+" does not contain a valid database entry.");//database+" is not an allowed database.");
  			}
  			string connString="",server="",database="",mysqlUser="",mysqlPassword="",mysqlUserLow="",mysqlPasswordLow="";
  			XPathNavigator navConString=navConn.SelectSingleNode("ConnectionString");
  			if(navConString!=null) {//If there is a connection string then use it.
  				connString=navConString.Value;
  			}
  			else {
  				//return navOne.SelectSingleNode("summary").Value;
  				//now, get the values for this connection
  				server=navConn.SelectSingleNode("ComputerName").Value;
  				database=navConn.SelectSingleNode("Database").Value;
  				mysqlUser=navConn.SelectSingleNode("User").Value;
  				mysqlPassword=navConn.SelectSingleNode("Password").Value;
  				mysqlUserLow=navConn.SelectSingleNode("UserLow").Value;
  				mysqlPasswordLow=navConn.SelectSingleNode("PasswordLow").Value;
  			}
  			XPathNavigator dbTypeNav=navConn.SelectSingleNode("DatabaseType");
  			DatabaseType dbtype=DatabaseType.MySql;
  			if(dbTypeNav!=null){
  				if(dbTypeNav.Value=="Oracle"){
  					dbtype=DatabaseType.Oracle;
  				}
  			}
  			DataConnection dcon=new DataConnection();
  			if(connString!="") {
  				try {
  					dcon.SetDb(connString,"",dbtype);
  				}
  				catch(Exception e) {
  					throw new Exception(e.Message+"\r\n"+"Connection to database failed.  Check the values in the config file on the web server "+configFilePath);
  				}
  			}
  			else {
  				try {
  					dcon.SetDb(server,database,mysqlUser,mysqlPassword,mysqlUserLow,mysqlPasswordLow,dbtype);
  				}
  				catch(Exception e) {
  					throw new Exception(e.Message+"\r\n"+"Connection to database failed.  Check the values in the config file on the web server "+configFilePath);
  				}
  			}
  			//todo?: make sure no users have blank passwords.
  		}
  
  		/*
  		///<summary>Used by the SL logon window to validate credentials.  Send in the password unhashed.  If invalid, it will always throw an exception of some type.  If it is valid, then it will return the hashed password.  This is the only place where the config file is read, and it's only read on startup.  So the web service needs to be restarted if the config file changes.</summary>
  		public static string CheckDbUserPassword(string configFilePath,string username,string password){
  			//for some reason, this static variable was remaining true even if the webservice was restarted.
  			//So we're not going to use it anymore.  Always load from file.
  			//if(!webServerConfigHasLoadedd){
  				LoadDatabaseInfoFromFile(configFilePath);
  			//	webServerConfigHasLoadedd=true;
  			//}
  			DataConnection dcon=new DataConnection();
  			//Then, check username and password
  			string passhash="";
  			string command="SELECT Password FROM userod WHERE UserName='"+POut.PString(username)+"'";
  			DataTable table=dcon.GetTable(command);
  			if(table.Rows.Count!=0){
  				passhash=table.Rows[0][0].ToString();
  			}
  			if(passhash=="" || passhash!=EncryptPassword(password)){
  				throw new Exception("Invalid username or password.");
  			}
  			return passhash;
  		}*/
  
  		///<summary></summary>
  		public static string EncryptPassword(string inputPass) {
  			//No need to check RemotingRole; no call to db.
  			bool useEcwAlgorithm=Programs.IsEnabled(ProgramName.eClinicalWorks);
  			return EncryptPassword(inputPass,useEcwAlgorithm);
  		}
  
  		///<summary>Creates a hash.</summary>
  		public static string EncryptPassword(string inputPass,bool useEcwAlgorithm) {
  			//No need to check RemotingRole; no call to db.
  			if(inputPass=="") {
  				return "";
  			}
  			HashAlgorithm algorithm=HashAlgorithm.Create("MD5");
  			if(useEcwAlgorithm){// && Programs.IsEnabled("eClinicalWorks")) {
  				byte[] asciiBytes=Encoding.ASCII.GetBytes(inputPass);
  				byte[] hashbytes=algorithm.ComputeHash(asciiBytes);//length=16
  				byte digit1;
  				byte digit2;
  				string char1;
  				string char2;
  				StringBuilder strbuild=new StringBuilder();
  				for(int i=0;i<hashbytes.Length;i++) {
  					if(hashbytes[i]==0) {
  						digit1=0;
  						digit2=0;
  					}
  					else {
  						digit1=(byte)Math.Floor((double)hashbytes[i]/16d);
  						//double remainder=Math.IEEERemainder((double)hashbytes[i],16d);
  						digit2=(byte)(hashbytes[i]-(byte)(16*digit1));
  					}
  					char1=ByteToStr(digit1);
  					char2=ByteToStr(digit2);
  					strbuild.Append(char1);
  					strbuild.Append(char2);
  				}
  				return strbuild.ToString();
  			}
  			else {//typical
  				byte[] unicodeBytes=Encoding.Unicode.GetBytes(inputPass);
  				byte[] hashbytes2=algorithm.ComputeHash(unicodeBytes);
  				return Convert.ToBase64String(hashbytes2);
  			}
  		}
  
  
  		///<summary>The only valid input is a value between 0 and 15.  Text returned will be 1-9 or a-f.</summary>
  		private static string ByteToStr(Byte byteVal) {
  			//No need to check RemotingRole; no call to db.
  			switch(byteVal) {
  				case 10:
  					return "a";
  				case 11:
  					return "b";
  				case 12:
  					return "c";
  				case 13:
  					return "d";
  				case 14:
  					return "e";
  				case 15:
  					return "f";
  				default:
  					return byteVal.ToString();
  			}
  		}
  
  		///<summary>Used from log on screen, phoneUI, and when logging in via command line.</summary>
  		public static bool CheckTypedPassword(string inputPass,string hashedPass) {
  			//No need to check RemotingRole; no call to db.
  			if(hashedPass=="") {
  				return inputPass=="";
  			}
  			string hashedInput=EncryptPassword(inputPass);
  			//MessageBox.Show(
  			//Debug.WriteLine(hashedInput+","+hashedPass);
  			return hashedInput==hashedPass;
  		}		
  
  		///<summary>usertype can be 'all', 'prov', 'emp', 'stu', 'inst', or 'other'.</summary>
  		public static List<Userod> RefreshSecurity(string usertype,long schoolClassNum) {
  			if(RemotingClient.RemotingRole==RemotingRole.ClientWeb) {
  				return Meth.GetObject<List<Userod>>(MethodBase.GetCurrentMethod(),usertype,schoolClassNum);
  			}
  			string command;
  			if(usertype=="stu" || usertype=="inst") {
  				command="SELECT userod.* FROM userod,provider "
  					+"WHERE userod.ProvNum=provider.ProvNum "
  					+"AND provider.IsInstructor=";
  				if(usertype=="inst") {
  					command+="1 ";
  				}
  				else {
  					command+="0 ";
  				}
  				if(usertype=="stu" && schoolClassNum>0) {
  					command+="AND SchoolClassNum="+POut.Long(schoolClassNum)+" ";
  				}
  				command+="ORDER BY UserName";
  				return Crud.UserodCrud.SelectMany(command);
  			}
  			command="SELECT * FROM userod WHERE UserNumCEMT=0  ";
  			if(usertype=="emp"){
  				command+="AND EmployeeNum!=0 ";
  			}
  			else if(usertype=="prov") {//and all schoolclassnums
  				command+="AND ProvNum!=0 ";
  			}
  			else if(usertype=="all") {
  				//command+="";
  			}
  			else if(usertype=="other") {
  				command+="AND ProvNum=0 AND EmployeeNum=0 ";
  			}
  			command+="ORDER BY UserName";
  			return Crud.UserodCrud.SelectMany(command);
  		}
  
  		///<summary>Updates all students/instructors to the specified user group.  Surround with try/catch because it can throw exceptions.</summary>
  		public static void UpdateUserGroupsForDentalSchools(UserGroup userGroup,bool isInstructor) {
  			if(RemotingClient.RemotingRole==RemotingRole.ClientWeb) {
  				Meth.GetVoid(MethodBase.GetCurrentMethod(),userGroup,isInstructor);
  				return;
  			}
  			string command;
  			//Check if the user group that the students or instructors are trying to go to has the SecurityAdmin permission.
  			if(!GroupPermissions.HasPermission(userGroup.UserGroupNum,Permissions.SecurityAdmin)) {
  				//We need to make sure that moving these users to the new user group does not eliminate all SecurityAdmin users in db.
  				command="SELECT COUNT(*) FROM userod "
  					+"INNER JOIN usergroup ON userod.UserGroupNum=usergroup.UserGroupNum "
  					+"INNER JOIN grouppermission ON grouppermission.UserGroupNum=usergroup.UserGroupNum "
  					+"WHERE userod.UserNum NOT IN "
  					+"(SELECT userod.UserNum FROM userod,provider "
  
gt: 						+"WHERE userod.ProvNum=provider.ProvNum ";