using System;
using System.IO;
using System.Data;
using System.Collections; 
 
namespace IMLibrary.FTPLibrary
{
	/// <summary>
	/// Virtual ftp server.internal
	/// </summary>
    public class APP_FTPServer
	{
		private FTP_Server m_pServer = null;
		private string     m_FtpRoot = "";

		/// <summary>
		/// Default constructor.
		/// </summary>
		/// <param name="ftpRootDir">Ftp root dir for this server.</param>
		/// <param name="ip">IP address on which to listen.</param>
		/// <param name="port">Port on which to listen.</param>
		public APP_FTPServer(string ftpRootDir,string ip,int port)
		{
			m_FtpRoot = ftpRootDir;

			m_pServer = new  FTP_Server();

			this.m_pServer.CommandIdleTimeOut = 60000;
			this.m_pServer.LogCommands = false;
			this.m_pServer.MaxBadCommands = 30;
			this.m_pServer.SessionIdleTimeOut = 800000;
			this.m_pServer.Port = port;
			this.m_pServer.IpAddress = ip;
			this.m_pServer.CreateDir += new  FileSysEntryEventHandler(this.OnCreateDir);
			this.m_pServer.StoreFile += new  FileSysEntryEventHandler(this.OnStoreFile);
			this.m_pServer.DeleteDir += new FileSysEntryEventHandler(this.OnDeleteDir);
			//this.m_pServer.SysError += new   FTPLibrary.FileSysEntryEventHandler(this.OnSysError);
			this.m_pServer.DirExists += new  FileSysEntryEventHandler(this.OnDirExists);
			this.m_pServer.FileExists += new  FileSysEntryEventHandler(this.OnFileExists);
			this.m_pServer.RenameDirFile += new  FileSysEntryEventHandler(this.OnRenameDirFile);
			this.m_pServer.DeleteFile += new  FileSysEntryEventHandler(this.OnDeleteFile);
            //this.m_pServer.ValidateIPAddress += new IMLibrary.NetProtocol.ValidateIPHandler(this.OnValidateIP);
            this.m_pServer.GetFileSize += new FileSysEntryEventHandler(this.OnGetFileSize);
            this.m_pServer.GetFile += new  FileSysEntryEventHandler(this.OnGetFile);
			this.m_pServer.GetDirInfo += new FileSysEntryEventHandler(this.OnGetDirInfo);
			this.m_pServer.AuthUser += new  AuthUserEventHandler(this.OnAuthUser);
           
            m_pServer.Enabled = true;
		}

   


        #region Events handling

        #region FTP server events

        #region method OnAuthUser

        //public void OnValidateIP(object sender,ValidateIP_EventArgs e)
        //{			
        //	e.Validated = true;
        //}

        #endregion

        #region method OnAuthUser

        public void OnAuthUser(object sender, AuthUser_EventArgs e)
        {
            if (e.UserName.ToLower() == "ftp" && e.PasswData == "123")
                e.Validated = true;
            //if(Impersonate.LogonAndImpersonateUser(e.UserName,e.PasswData)){
            //	e.Validated = true;
            //}
            else
            {
                e.Validated = false;
            }
        }

		#endregion


		#region method OnGetDirInfo
        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
		public void OnGetDirInfo(object sender,FileSysEntry_EventArgs e)
		{			
			try{				
				DataTable dt = e.DirInfo.Tables["DirInfo"];

				string physicalPath = GetPhysicalPath(e.Name);

				// Add directories
				if(Directory.Exists(physicalPath)){
					string[] dirs = Directory.GetDirectories(physicalPath);
					foreach(string d in dirs){
						DataRow dr = dt.NewRow();
						dr["Name"] = new DirectoryInfo(d).Name;
						dr["Date"] = Directory.GetCreationTime(d);
					//	dr["Size"] = "";
						dr["IsDirectory"] = true;

						dt.Rows.Add(dr);
					}
					
					// Add virtual folders
					if(File.Exists(physicalPath + "__Config_ftp.xml")){
						try{
							DataSet ds = new DataSet();
							ds.ReadXml(physicalPath + "__Config_ftp.xml");

							foreach(DataRow dr in ds.Tables["virtualFolder"].Rows){
								// ToDo: if virtual folder is same name as physical folder

								// Add vitual folder to list only if it exists
								string vDirPhysicalPath = dr["path"].ToString();
								if(Directory.Exists(vDirPhysicalPath)){
									DataRow drX = dt.NewRow();
									drX["Name"] = dr["name"].ToString();
									drX["Date"] = Directory.GetCreationTime(vDirPhysicalPath);
								//	drX["Size"] = "";
									drX["IsDirectory"] = true;

									dt.Rows.Add(drX);
								}
							}
						}
						catch{
						}
					}

					// Add files
					string[] files = Directory.GetFiles(physicalPath);
					foreach(string f in files){
						// Hide config file
						if(Path.GetFileName(f).ToLower() != "__config_ftp.xml"){
							DataRow dr = dt.NewRow();
							dr["Name"] = Path.GetFileName(f);
							dr["Date"] = File.GetCreationTime(f);
							dr["Size"] = new FileInfo(f).Length;
							dr["IsDirectory"] = false;

							dt.Rows.Add(dr);
						}
					}
					
				}
			}
			catch{
				e.Validated = false;
			}
		}

		#endregion

		#region method OnDirExists

		public void OnDirExists(object sender,FileSysEntry_EventArgs e)
		{			
			if(!Directory.Exists(GetPhysicalPath(e.Name))){
				e.Validated = false;
			}
		}

		#endregion

		#region method OnCreateDir

		public void OnCreateDir(object sender,FileSysEntry_EventArgs e)
		{	
			if(Directory.Exists(GetPhysicalPath(e.Name))){
				e.Validated = false;
			}
			else{
				Directory.CreateDirectory(GetPhysicalPath(e.Name));
			}
		}

		#endregion

		#region method OnDeleteDir

		public void OnDeleteDir(object sender,FileSysEntry_EventArgs e)
		{		
			if(!IsVirtualDir(e.Name) && Directory.Exists(GetPhysicalPath(e.Name))){				
				Directory.Delete(GetPhysicalPath(e.Name));				
			}
			else{			
				e.Validated = false;
			}
		}

		#endregion

		#region method OnRenameDirFile

		public void OnRenameDirFile(object sender,FileSysEntry_EventArgs e)
		{
			// Remove last /
			string to   = e.NewName.Substring(0,e.NewName.Length - 1);
			string from = e.Name.Substring(0,e.Name.Length - 1);

			if(IsVirtualDir(to) || IsVirtualDir(from) || Directory.Exists(GetPhysicalPath(to)) || File.Exists(GetPhysicalPath(to))){
				e.Validated = false;
			}
			else{
				if(Directory.Exists(GetPhysicalPath(from))){
					Directory.Move(GetPhysicalPath(from),GetPhysicalPath(to));
				} 
				else if(File.Exists(GetPhysicalPath(from))){
					File.Move(GetPhysicalPath(from),GetPhysicalPath(to));
				}
			}			
		}

		#endregion


		#region method OnFileExists

		public void OnFileExists(object sender,FileSysEntry_EventArgs e)
		{	
			try{
				if(File.Exists(GetPhysicalPath(e.Name))){
					e.Validated = true;
				}
			}
			catch{
				e.Validated = false;
			}
		}

		#endregion

		#region method OnGetFile

		public void OnGetFile(object sender,FileSysEntry_EventArgs e)
		{	
			try{
				if(File.Exists(GetPhysicalPath(e.Name))){
					e.FileStream = File.OpenRead(GetPhysicalPath(e.Name));
                    Console.WriteLine(e.FileStream.Length);
				}
			}
			catch{
				e.Validated = false;
			}
		}

        #endregion

        #region method OnGetFileSize 
        public void OnGetFileSize(object sender, FileSysEntry_EventArgs e)
        {
            try
            {
                if (File.Exists(GetPhysicalPath(e.Name)))
                {
                    e.Length = new FileInfo(GetPhysicalPath(e.Name)).Length;
                }
                else
                    e.Length = 0;
            }
            catch
            {
                e.Length = -1;
            }
        }

        #endregion

        #region method OnStoreFile

        public void OnStoreFile(object sender,FileSysEntry_EventArgs e)
		{	
			try{
				if(!File.Exists(GetPhysicalPath(e.Name))){
					e.FileStream = File.Create(GetPhysicalPath(e.Name));
				}
			}
			catch{
				e.Validated = false;
			}
		}

		#endregion

		#region method OnDeleteFile

		public void OnDeleteFile(object sender,FileSysEntry_EventArgs e)
		{			
			if(File.Exists(GetPhysicalPath(e.Name))){
				File.Delete(GetPhysicalPath(e.Name));
			}
			else{
				e.Validated = false;
			}
		}

		#endregion


		#region method OnSysError

		//public void OnSysError(object sender,Error_EventArgs e)
		//{			
		//	Error.DumpError(e.Exception,e.StackTrace);
		//}

		#endregion
		
		#endregion

		#endregion


		#region method GetPhysicalPath

		/// <summary>
		/// Gets physical path from absolute path (replaces virtual folders with real path).
		/// </summary>
		/// <param name="path"></param>
		/// <returns></returns>
		private string GetPhysicalPath(string path)
		{
			string pPath = m_FtpRoot + "\\";

			bool endsWithSep = false;

			if(path.StartsWith("/")){
				path = path.Substring(1,path.Length - 1);
			}
			if(path.EndsWith("/")){
				path = path.Substring(0,path.Length - 1);
				endsWithSep = true;
			}

			string[] pathParts = path.Split('/','\\');
			foreach(string part in pathParts){
				if(part.Length > 0){
					// This is physical directory
					if(Directory.Exists(pPath + part)){					
						pPath += part + "/";
					}
					else{
						// See if virtual folder
						if(File.Exists(pPath + "__Config_ftp.xml")){						
							try{
								DataSet ds = new DataSet();
								ds.ReadXml(pPath + "__Config_ftp.xml");

								ds.Tables["virtualFolder"].DefaultView.RowFilter = "name='" + part + "'";
								if(ds.Tables["virtualFolder"].DefaultView.Count > 0){								
									pPath = ds.Tables["virtualFolder"].DefaultView[0]["path"].ToString();
									
									if(!pPath.EndsWith("\\")){
										pPath += "/"; 
									}
								}
								else{
									pPath += part + "/";
								}
							}
							catch{
							}
						}
						else{
							pPath += part + "/";
						}
					}
				}
			}

			if(!endsWithSep && pPath.EndsWith("/")){
				pPath = pPath.Substring(0,pPath.Length - 1);
			}

			return pPath;
		}

		#endregion

		#region method IsVirtualDir

		/// <summary>
		/// Gets if specified dir is virtual directory.
		/// </summary>
		/// <param name="dir"></param>
		/// <returns></returns>
		private bool IsVirtualDir(string dir)
		{
			if(dir.StartsWith("/")){
				dir = dir.Substring(1,dir.Length - 1);
			}
			if(dir.EndsWith("/")){
				dir = dir.Substring(0,dir.Length - 1);
			}

			string pPath = "";

			//--- Move dir up and get physical path
			string[] pathParts = dir.Split('/','\\');
			if(pathParts.Length > 1){
		//		pPath = "";
				for(int i=0;i<(pathParts.Length - 1);i++){
					pPath += pathParts[i] + "/";
				}

				if(pPath.Length == 0){
					pPath = "/";
				}
			}
		//	else{
		//		pPath = "";
		//	}

			pPath = GetPhysicalPath(pPath);
			//---------------------------------------------

			if(!Directory.Exists(pPath + pathParts[pathParts.Length - 1])){				
				// See if virtual folder
				if(File.Exists(pPath + "__Config_ftp.xml")){
					try{
						DataSet ds = new DataSet();
						ds.ReadXml(pPath + "__Config_ftp.xml");

						ds.Tables["virtualFolder"].DefaultView.RowFilter = "name='" + pathParts[pathParts.Length - 1] + "'";
						if(ds.Tables["virtualFolder"].DefaultView.Count > 0){								
							return true;
						}
					}
					catch{
					}
				}
			}

			return false;
		}

		#endregion


		#region method EndServer

		/// <summary>
		/// Stops server and does clean up.
		/// </summary>
		public void EndServer()
		{
			if(this.m_pServer != null){
				this.m_pServer.Enabled = false;
				this.m_pServer         = null;
			}
		}

		#endregion
	}
}
