text: /*
   * Part of WCM Commander
   * https://github.com/corporateshark/WCMCommander
   * wcm@linderdaum.com
   */
  
  #pragma once
  
  #include "vfs.h"
  #include "tcp_sock.h"
  
  
  enum FTP_ERRNO
  {
  	EFTP_BADPROTO  = -10,
  	EFTP_NOTSUPPORTED = -11,
  	EFTP_OUTOFRESOURCE_INTERNAL = -12,
  	EFTP_INVALID_PARAMETER = -13,
  	EFTP_NOTEXIST = -14,
  	EFTP_EXIST = -15
  };
  
  //++volatile надо скорректировать
  class FtpStatCache
  {
  	enum CONSTS { CACHE_TIMEOUT = 60 };
  	struct Dir: public iIntrusiveCounter
  	{
  		time_t tim;
  		cstrhash< clPtr<Dir>, char> dirs;
  		clPtr< cstrhash<FSStat, char> >  statHash;
  		Dir(): tim( 0 ) {}
  	};
  
  	Mutex mutex;
  	Dir root;
  	int* pCharset;
  	Dir* GetParent( FSPath& path );
  public:
  	FtpStatCache( int* pCs ): pCharset( pCs ) {}
  	void PutStat( FSPath path, clPtr<cstrhash<FSStat, char> > statHash );
  	int GetStat( FSPath path, FSStat* st ); //0 - ok, 1 - not cache for it, -1 - not exist in cache
  	void Del( FSPath path );
  private:
  	FtpStatCache() {};
  	FtpStatCache( const FtpStatCache& ) {};
  	void operator = ( const FtpStatCache& ) {};
  };
  
  //++volatile надо скорректировать
  class FTPNode: public iIntrusiveCounter
  {
  	TCPSyncBufProto ctrl;
  	TCPSyncBufProto data;
  
  	bool _passive;
  
  	int ReadCode();
  	void Passive( unsigned& passiveIp, int& passivePort );
  	void Port( unsigned ip, int port );
  	void OpenData( const char* cmd );
  public:
  	FTPNode();
  
  	void Connect( unsigned ip, int port, const char* user, const char* password, bool passive );
  	void Noop();
  	void Ls( ccollect<std::string >& list );
  	void Cwd( const char* path );
  	void OpenRead( const char* fileName );
  	void MkDir( const char* fileName );
  	void RmDir( const char* fileName );
  	void Rename( const char* from, const char* to );
  	void OpenWrite( const char* fileName );
  	int ReadData( void* s, int size ) { return data.Read( ( char* )s, size ); }
  	void WriteData( void* s, int size ) { data.Write( ( char* )s, size ); }
  	void CloseData();
  	void Delete( const char* fileName );
  
  	void SetStopFunc( CheckStopFunc f, void* d ) { ctrl.SetStopFunc( f, d ); data.SetStopFunc( f, d ); }
  	void ClearStopFunc() { SetStopFunc( 0, 0 ); }
  
  	~FTPNode();
  };
  
  
  class FtpIDCollection
  {
  	Mutex mutex;
  	cstrhash<int, unicode_t> byName;
  	ccollect<std::vector<unicode_t> > byId;
  	static unicode_t u0;
  public:
  	FtpIDCollection() {}
  
  	const unicode_t* GetName( int id )
  	{
  		MutexLock lock( &mutex );
  		return ( id >= 0 && id < byId.count() && byId[id].data() ) ? byId[id].data() : &u0;
  	}
  
  	int GetId( const unicode_t* name )
  	{
  		MutexLock lock( &mutex );
  		int* n = byName.exist( name );
  
  		if ( n ) { return *n; }
  
  		std::vector<unicode_t> p = new_unicode_str( name );
  		int t = byId.count();
  		byId.append( p );
  		byName[name] = t;
  		return t;
  	}
  	~FtpIDCollection() {}
  };
  
  //++volatile надо скорректировать
  class FSFtp : public FS
  {
  	mutable Mutex infoMutex;
  	FSFtpParam _infoParam;
  
  	FtpStatCache statCache; //has own mutex
  	FtpIDCollection uids;   //has own mutex
  	FtpIDCollection gids;   //has own mutex
  
  	Mutex mutex;
  	FSFtpParam _param;
  
  	enum { NODES_COUNT = 32 };
  
  	struct Node: public iIntrusiveCounter
  	{
  		volatile bool busy;
  		volatile time_t lastCall;
  		/*volatile*/ clPtr<FTPNode> pFtpNode;
  
  		Node(): busy( false ), lastCall( 0 ) {}
  	};
  
  	Node nodes[NODES_COUNT];
  
  	int GetFreeNode( int* err, FSCInfo* info ); //return -1 if error
  
  	int ReadDir_int ( FSList* list, cstrhash<FSStat, char>* pSList, FSPath& _path, int* err, FSCInfo* info );
  public:
  	FSFtp( FSFtpParam* param );
  
  	virtual unsigned Flags();
  	virtual bool IsEEXIST( int err );
  	virtual bool IsENOENT( int err );
  	virtual bool IsEXDEV( int err );
  	virtual FSString StrError( int err );
  	virtual bool Equal( FS* fs );
  
  	virtual int OpenRead ( FSPath& path, int flags, int* err, FSCInfo* info );
  	virtual int OpenCreate  ( FSPath& path, bool overwrite, int mode, int flags, int* err, FSCInfo* info );
  	virtual int Close ( int fd, int* err, FSCInfo* info );
  	virtual int Read  ( int fd, void* buf, int size, int* err, FSCInfo* info );
  	virtual int Write ( int fd, void* buf, int size, int* err, FSCInfo* info );
  	virtual int Rename   ( FSPath&  oldpath, FSPath& newpath, int* err,  FSCInfo* info );
  	virtual int MkDir ( FSPath& path, int mode, int* err,  FSCInfo* info );
  	virtual int Delete   ( FSPath& path, int* err, FSCInfo* info );
  	virtual int RmDir ( FSPath& path, int* err, FSCInfo* info );
  	virtual int SetFileTime ( FSPath& path, FSTime cTime, FSTime aTime, FSTime mTime, int* err, FSCInfo* info ) override;
  	virtual int ReadDir  ( FSList* list, FSPath& path, int* err, FSCInfo* info );
  	virtual int Stat  ( FSPath& path, FSStat* st, int* err, FSCInfo* info );
  
  	virtual FSString Uri( FSPath& path );
  	virtual ~FSFtp();
  
  	FSFtpParam GetParamValue() const { MutexLock lock( &infoMutex ); return _infoParam; }
  
  	virtual unicode_t* GetUserName( int user, unicode_t buf[64] );
  	virtual unicode_t* GetGroupName( int group, unicode_t buf[64] );
  };
  