/// <summary>Connect to the FTP server using configuration parameters</summary> /// <returns>An FTPClient instance</returns> /// <exception cref="System.IO.IOException"/> private FTPClient Connect() { FTPClient client = null; Configuration conf = GetConf(); string host = conf.Get(FsFtpHost); int port = conf.GetInt(FsFtpHostPort, FTP.DefaultPort); string user = conf.Get(FsFtpUserPrefix + host); string password = conf.Get(FsFtpPasswordPrefix + host); client = new FTPClient(); client.Connect(host, port); int reply = client.GetReplyCode(); if (!FTPReply.IsPositiveCompletion(reply)) { throw NetUtils.WrapException(host, port, NetUtils.UnknownHost, 0, new ConnectException ("Server response " + reply)); } else { if (client.Login(user, password)) { client.SetFileTransferMode(FTP.BlockTransferMode); client.SetFileType(FTP.BinaryFileType); client.SetBufferSize(DefaultBufferSize); } else { throw new IOException("Login failed on server - " + host + ", port - " + port + " as user '" + user + "'"); } } return(client); }
FTPReply?ReadNextReply() { if (CachedReply.Count > 0) { return(CachedReply.Dequeue()); } string streamstring = null; try { streamstring = MyFTPHelper.ReadFromNetStream(controlStream); } catch (Exception exc) { throw exc; } string[] messages = streamstring.Split(new string[] { MyFTPHelper.FTPNewLine }, StringSplitOptions.RemoveEmptyEntries); Array.ForEach(messages, (m) => CachedReply.Enqueue(FTPReply.String2Reply(m))); if (CachedReply.Count > 0) { return(CachedReply.Dequeue()); } else { return(null); } }
/// <summary> /// A stream obtained via this call must be closed before using other APIs of /// this class or else the invocation will block. /// </summary> /// <exception cref="System.IO.IOException"/> public override FSDataOutputStream Create(Path file, FsPermission permission, bool overwrite, int bufferSize, short replication, long blockSize, Progressable progress ) { FTPClient client = Connect(); Path workDir = new Path(client.PrintWorkingDirectory()); Path absolute = MakeAbsolute(workDir, file); FileStatus status; try { status = GetFileStatus(client, file); } catch (FileNotFoundException) { status = null; } if (status != null) { if (overwrite && !status.IsDirectory()) { Delete(client, file, false); } else { Disconnect(client); throw new FileAlreadyExistsException("File already exists: " + file); } } Path parent = absolute.GetParent(); if (parent == null || !Mkdirs(client, parent, FsPermission.GetDirDefault())) { parent = (parent == null) ? new Path("/") : parent; Disconnect(client); throw new IOException("create(): Mkdirs failed to create: " + parent); } client.Allocate(bufferSize); // Change to parent directory on the server. Only then can we write to the // file on the server by opening up an OutputStream. As a side effect the // working directory on the server is changed to the parent directory of the // file. The FTP client connection is closed when close() is called on the // FSDataOutputStream. client.ChangeWorkingDirectory(parent.ToUri().GetPath()); FSDataOutputStream fos = new _FSDataOutputStream_263(this, client, client.StoreFileStream (file.GetName()), statistics); if (!FTPReply.IsPositivePreliminary(client.GetReplyCode())) { // The ftpClient is an inconsistent state. Must close the stream // which in turn will logout and disconnect from FTP server fos.Close(); throw new IOException("Unable to create file: " + file + ", Aborting"); } return(fos); }
/// <exception cref="System.IO.IOException"/> public override FSDataInputStream Open(Path file, int bufferSize) { FTPClient client = Connect(); Path workDir = new Path(client.PrintWorkingDirectory()); Path absolute = MakeAbsolute(workDir, file); FileStatus fileStat = GetFileStatus(client, absolute); if (fileStat.IsDirectory()) { Disconnect(client); throw new FileNotFoundException("Path " + file + " is a directory."); } client.Allocate(bufferSize); Path parent = absolute.GetParent(); // Change to parent directory on the // server. Only then can we read the // file // on the server by opening up an InputStream. As a side effect the working // directory on the server is changed to the parent directory of the file. // The FTP client connection is closed when close() is called on the // FSDataInputStream. client.ChangeWorkingDirectory(parent.ToUri().GetPath()); InputStream @is = client.RetrieveFileStream(file.GetName()); FSDataInputStream fis = new FSDataInputStream(new FTPInputStream(@is, client, statistics )); if (!FTPReply.IsPositivePreliminary(client.GetReplyCode())) { // The ftpClient is an inconsistent state. Must close the stream // which in turn will logout and disconnect from FTP server fis.Close(); throw new IOException("Unable to open file: " + file + ", Aborting"); } return(fis); }
public void ReadServerReply() { while (true) { try { FTPReply?nreply = ReadNextReply(); if (nreply == null) { continue; } else { FTPReply reply = nreply.Value; PostMessageToConsoleWithLock("服务器返回值:" + reply.replyCode); switch (reply.replyCode) { case FTPReply.Code_FileList: List <string> fileList = MyFTPHelper.DecodeFileList(reply.post); this.fileList = fileList; PostMessageToConsoleWithLock("更新服务器文件目录"); break; case FTPReply.Code_UserNotLogIn: PostMessageToConsoleWithLock("由于账号未登录,命令无效"); break; } break; } } catch (Exception exc) { throw exc; } } }
/// <summary> /// Run a site-specific command on the /// server. Support for commands is dependent /// on the server /// </summary> /// <param name="command"> /// the site command to run /// </param> /// <returns> true if command ok, false if /// command not implemented /// </returns> public bool Site(string command) { // send the retrieve command string reply = control.SendCommand("SITE " + command); // Can get a 200 (ok) or 202 (not impl). Some // FTP servers return 502 (not impl) string[] validCodes = new string[]{"200", "202", "502"}; lastValidReply = control.ValidateReply(reply, validCodes); // return true or false? 200 is ok, 202/502 not // implemented if (reply.Substring(0, (3) - (0)).Equals("200")) return true; else return false; }
/// <summary> /// Delete the specified remote working directory /// </summary> /// <param name="dir"> /// name of remote directory to delete /// </param> public void Rmdir(string dir) { string reply = control.SendCommand("RMD " + dir); // some servers return 257, technically incorrect but // we cater for it ... string[] validCodes = new string[]{"250", "257"}; lastValidReply = control.ValidateReply(reply, validCodes); }
/// <summary> /// Rename a file or directory /// </summary> /// <param name="from"> name of file or directory to rename /// </param> /// <param name="to"> intended name /// /// </param> public void Rename(string from, string to) { string reply = control.SendCommand("RNFR " + from); lastValidReply = control.ValidateReply(reply, "350"); reply = control.SendCommand("RNTO " + to); lastValidReply = control.ValidateReply(reply, "250"); }
/// <summary> /// Issue arbitrary ftp commands to the FTP server. /// </summary> /// <param name="command"> /// ftp command to be sent to server /// </param> /// <param name="validCodes"> /// valid return codes for this command /// </param> public void Quote(string command, string[] validCodes) { string reply = control.SendCommand(command); // allow for no validation to be supplied if (validCodes != null && validCodes.Length > 0) lastValidReply = control.ValidateReply(reply, validCodes); }
/// <summary> /// Request to the server that the get is set up /// </summary> /// <param name="remoteFile"> /// name of remote file /// </param> private void InitGet(string remoteFile) { // set up data channel data = control.CreateDataSocket(connectMode); // send the retrieve command string reply = control.SendCommand("RETR " + remoteFile); // Can get a 125 or a 150 string[] validCodes1 = new string[]{"125", "150"}; lastValidReply = control.ValidateReply(reply, validCodes1); }
/// <summary> /// Delete the specified remote file /// </summary> /// <param name="remoteFile"> name of remote file to /// delete /// /// </param> public void Delete(string remoteFile) { string reply = control.SendCommand("DELE " + remoteFile); lastValidReply = control.ValidateReply(reply, "250"); }
/// <summary> /// Request the server to set up the put /// </summary> /// <param name="remoteFile"> name of remote file in /// current directory /// </param> /// <param name="append"> true if appending, false otherwise /// /// </param> private void InitPut(string remoteFile, bool append) { // set up data channel data = control.CreateDataSocket(connectMode); // send the command to store string cmd = append ? "APPE ":"STOR "; string reply = control.SendCommand(cmd + remoteFile); // Can get a 125 or a 150 string[] validCodes = new string[]{"125", "150"}; lastValidReply = control.ValidateReply(reply, validCodes); }
/// <summary> /// Validate that the put() or get() was successful /// </summary> private void ValidateTransfer() { // check the control response string[] validCodes = new string[]{"226", "250"}; string reply = control.ReadReply(); lastValidReply = control.ValidateReply(reply, validCodes); }
/// <summary> /// Get modification time for a remote file /// </summary> /// <param name="remoteFile"> /// name of remote file /// </param> /// <returns> /// modification time of file as a date /// </returns> public DateTime ModTime(string remoteFile) { string reply = control.SendCommand("MDTM " + remoteFile); lastValidReply = control.ValidateReply(reply, "213"); // parse the reply string ... DateTime ts = DateTime.ParseExact(lastValidReply.ReplyText, dtFormat, null); return ts; }
/// <summary> /// Create the specified remote working directory /// </summary> /// <param name="dir"> /// name of remote directory to create /// </param> public void Mkdir(string dir) { string reply = control.SendCommand("MKD " + dir); lastValidReply = control.ValidateReply(reply, "257"); }
/// <summary> /// Login into an account on the FTP server. This /// call completes the entire login process /// </summary> /// <param name="user"> /// user name /// </param> /// <param name="password"> /// user's password /// </param> public void Login(string user, string password) { string response = control.SendCommand("USER " + user); lastValidReply = control.ValidateReply(response, new string[] { "230", "331" }); if (lastValidReply.ReplyCode == "331") { response = control.SendCommand("PASS " + password); lastValidReply = control.ValidateReply(response, "230"); } }
/// <summary> /// Get the help text for the specified command /// </summary> /// <param name="command"> name of the command to get help on /// </param> /// <returns> help text from the server for the supplied command /// /// </returns> public string Help(string command) { string reply = control.SendCommand("HELP " + command); string[] validCodes = new string[]{"211", "214"}; lastValidReply = control.ValidateReply(reply, validCodes); return lastValidReply.ReplyText; }
/// <summary> /// List a directory's contents as an array of strings. A detailed /// listing is available, otherwise just filenames are provided. /// The detailed listing varies in details depending on OS and /// FTP server. Note that a full listing can be used on a file /// name to obtain information about a file /// </summary> /// <param name="dirname"> /// name of directory (<b>not</b> a file mask) /// </param> /// <param name="full"> /// true if detailed listing required false otherwise /// </param> /// <returns> /// an array of directory listing strings /// </returns> public string[] Dir(string dirname, bool full) { // set up data channel data = control.CreateDataSocket(connectMode); // send the retrieve command string command = full?"LIST ":"NLST "; if (dirname != null) command += dirname; // some FTP servers bomb out if NLST has whitespace appended command = command.Trim(); string reply = control.SendCommand(command); // check the control response. wu-ftp returns 550 if the // directory is empty, so we handle 550 appropriately string[] validCodes1 = new string[]{"125", "150", "550"}; lastValidReply = control.ValidateReply(reply, validCodes1); // an empty array of files for 550 string[] result = new string[0]; // a normal reply ... extract the file list if (!lastValidReply.ReplyCode.Equals("550")) { // get an character input stream to read data from . StreamReader reader = new StreamReader(GetDataStream()); // read a line at a time ArrayList lines = new ArrayList(); string line = null; while ((line = reader.ReadLine()) != null) { lines.Add(line); } try { reader.Close(); } catch (IOException ignore) { } // check the control response string[] validCodes2 = new string[]{"226", "250"}; reply = control.ReadReply(); lastValidReply = control.ValidateReply(reply, validCodes2); // empty array is default if (lines.Count > 0) result = (string[]) lines.ToArray(typeof(string)); } return result; }
/// <summary> /// Get the type of the OS at the server /// </summary> /// <returns> /// the type of server OS /// </returns> public string System() { string reply = control.SendCommand("SYST"); lastValidReply = control.ValidateReply(reply, "215"); return lastValidReply.ReplyText; }
/// <summary> /// Supply the user name to log into an account /// on the FTP server. Must be followed by the /// password() method - but we allow for /// </summary> /// <param name="user"> /// user name /// </param> public void User(string user) { string reply = control.SendCommand("USER " + user); // we allow for a site with no password - 230 response string[] validCodes = new string[]{"230", "331"}; lastValidReply = control.ValidateReply(reply, validCodes); }
public static string Reply2String(FTPReply reply) { return(((int)reply.firstCode).ToString() + ((int)reply.secondCode).ToString() + reply.finerGradation.ToString() + " " + reply.post); }
/// <summary> /// Supplies the password for a previously supplied /// username to log into the FTP server. Must be /// preceeded by the user() method /// </summary> /// <param name="password"> /// user's password /// </param> public void Password(string password) { string reply = control.SendCommand("PASS " + password); // we allow for a site with no passwords (202) string[] validCodes = new string[]{"230", "202"}; lastValidReply = control.ValidateReply(reply, validCodes); }
/// <summary> /// Change the remote working directory to /// that supplied /// </summary> /// <param name="dir"> name of remote directory to /// change to /// /// </param> public void Chdir(string dir) { string reply = control.SendCommand("CWD " + dir); lastValidReply = control.ValidateReply(reply, "250"); }
private static void DoCustomCommand(FTPSClient client) { FTPReply reply = client.SendCustomCommand(commandArguments[0]); Console.WriteLine("Server reply: " + reply.ToString()); }
/// <summary> /// Get the current remote working directory /// </summary> /// <returns> /// the current working directory /// </returns> public string Pwd() { string reply = control.SendCommand("PWD"); lastValidReply = control.ValidateReply(reply, "257"); // get the reply text and extract the dir // listed in quotes, if we can find it. Otherwise // just return the whole reply string string text = lastValidReply.ReplyText; int start = text.IndexOf((System.Char) '"'); int end = text.LastIndexOf((System.Char) '"'); if (start >= 0 && end > start) return text.Substring(start + 1, (end) - (start + 1)); else return text; }
public override string ToString() { return(FTPReply.Reply2String(this)); }
public void Query(string searchString, Int32 offsetItems, Int32 isCaseSensitive, Int32 matchWholeWord, Int32 matchPath, Int32 maxItemsReturn) { string command = String.Format("QUERY {0} {1} {2} {3} {4} {5}", offsetItems, maxItemsReturn, isCaseSensitive, matchWholeWord, matchPath, searchString); string reply = control.SendCommand(command); // allow for no validation to be supplied string[] validCodes = new string[] { "200" }; lastValidReply = control.ValidateReply(reply, validCodes); }
/// <summary> /// Quit the FTP session /// </summary> public void Quit() { try { string reply = control.SendCommand("QUIT"); string[] validCodes = new string[]{"221", "226"}; lastValidReply = control.ValidateReply(reply, validCodes); } finally { // ensure we clean up the connection control.Logout(); control = null; } }
public void Start() { while (true) { try { FTPCommand?ncommand = ReadNextCommand(); if (ncommand == null) { continue; } else { FTPCommand command = ncommand.Value; FTPReply reply = new FTPReply() { replyCode = FTPReply.Code_SyntaxError }; switch (command.controlCommand) { case "USER": //USER 指定账号 if (command.parameters.Length != 1) { reply = new FTPReply() { replyCode = FTPReply.Code_SyntaxErrorPara, }; break; } user.username = command.parameters[0]; break; case "PASS": //PASS 指定密码 if (command.parameters.Length != 1) { reply = new FTPReply() { replyCode = FTPReply.Code_SyntaxErrorPara, }; break; } user.password = command.parameters[0]; if (serverDispatcher.CheckUserWithLock(user)) { Logined = true; serverDispatcher.PostMessageFromClient("已成功登录", this); reply = new FTPReply() { replyCode = FTPReply.Code_UserLoggedIn, post = "login success" }; } else { serverDispatcher.PostMessageFromClient("密码或用户名有误", this); reply = new FTPReply() { replyCode = FTPReply.Code_UserNotLogIn, post = "login fail" }; } break; case "LIST": //LIST 返回服务器的文件目录(标准中不指定返回格式,格式为我们自定义) reply = new FTPReply() { replyCode = FTPReply.Code_FileList, post = serverDispatcher.GetEncodedFileList() }; break; case "STOR": //STOR 客户端上传文件 if (command.parameters.Length != 1) { reply = new FTPReply() { replyCode = FTPReply.Code_SyntaxErrorPara }; break; } string filename = command.parameters[0]; FileStream downloadFileStream = File.OpenWrite(serverDispatcher.GetCurrentDirectory() + filename); NetworkStream downloadDataStream = dataClient.GetStream(); if (downloadFileStream == null) { reply = new FTPReply() { replyCode = FTPReply.Code_CantOopenDataConnection }; break; } if (dataClient == null) { reply = new FTPReply() { replyCode = FTPReply.Code_ConnectionClosed }; break; } currentTransfer = new FileTransfer() { networkStream = downloadDataStream, filestream = downloadFileStream, }; currentTransfer.DownloadAsync(() => { downloadDataStream.Close(); downloadFileStream.Close(); serverDispatcher.PostMessageFromClient("文件上传完成", this); } ); reply = new FTPReply() { replyCode = FTPReply.Code_ConnectionClosed }; break; case "RETR": //RETR 客户端下载文件 if (command.parameters.Length != 1) { reply = new FTPReply() { replyCode = FTPReply.Code_SyntaxErrorPara }; break; } FileStream uploadFileStream = serverDispatcher.OpenFileStreamInfileList(command.parameters[0]); NetworkStream uploadDataStream = dataClient.GetStream(); if (uploadFileStream == null) { reply = new FTPReply() { replyCode = FTPReply.Code_CantOopenDataConnection }; break; } if (dataClient == null) { reply = new FTPReply() { replyCode = FTPReply.Code_ConnectionClosed }; break; } currentTransfer = new FileTransfer() { networkStream = uploadDataStream, filestream = uploadFileStream, }; currentTransfer.UploadAsync(() => { uploadDataStream.Close(); uploadFileStream.Close(); serverDispatcher.PostMessageFromClient("文件上传完成", this); } ); reply = new FTPReply() { replyCode = FTPReply.Code_ConnectionClosed }; break; case "ABOR": //QUIT 关闭与服务器的连接 throw new NotImplementedException(); case "QUIT": //ABOR 放弃之前的文件传输 throw new NotImplementedException(); case "PASV": //PASV 数据线程让服务器监听特定端口 throw new NotImplementedException(); case "PORT": //PORT 客户端的控制端口为N,数据端口为N+1,服务器的控制端口为21,数据端口为20 if (command.parameters.Length != 1 || !int.TryParse(command.parameters[0], out controlPort)) { reply = new FTPReply() { replyCode = FTPReply.Code_SyntaxErrorPara }; break; } if (!serverDispatcher.CheckDataPortLegal(controlPort, this)) { reply = new FTPReply() { replyCode = FTPReply.Code_CantOopenDataConnection }; break; } var remoteDataEnd = (IPEndPoint)controlClient.Client.RemoteEndPoint; remoteDataEnd.Port = controlPort + 1; dataClient = new TcpClient(); reply = new FTPReply() { replyCode = FTPReply.Code_DataConnectionOpen }; dataClient.ConnectAsync(remoteDataEnd.Address.MapToIPv4(), remoteDataEnd.Port); serverDispatcher.PostMessageFromClient("与" + user.username + "建立数据连接", this); break; default: break; } MyFTPHelper.WriteToNetStream(reply.ToString(), controlStream); } } catch (System.IO.IOException exc) { serverDispatcher.PostMessageFromClient(exc.Message, this); controlClient.Close(); controlStream.Close(); return; } } }