/// <summary> /// Process an FTP command. /// </summary> private void ProcessCommand(string verb, string arguments) { switch (verb) { case "SYST": { Respond(215, "UNIX emulated by mooftpserv"); break; } case "QUIT": { Respond(221, "Bye."); // first flush, then close controlSocket.Shutdown(SocketShutdown.Both); controlSocket.Close(); break; } case "USER": { Respond(230, "You are already logged in."); break; } case "PASS": { Respond(230, "You are already logged in."); break; } case "FEAT": { Respond(211, "Features:\r\n " + String.Join("\r\n ", FEATURES), true); Respond(211, "Features done."); break; } case "OPTS": { // Windows Explorer uses lowercase args if (arguments != null && arguments.ToUpper() == "UTF8 ON") { Respond(200, "Always in UTF8 mode."); } else { Respond(504, "Unknown option."); } break; } case "TYPE": { if (arguments == "A" || arguments == "A N") { transferDataType = DataType.ASCII; Respond(200, "Switching to ASCII mode."); } else if (arguments == "I") { transferDataType = DataType.IMAGE; Respond(200, "Switching to BINARY mode."); } else { Respond(500, "Unknown TYPE arguments."); } break; } case "PORT": { IPEndPoint port = ParseAddress(arguments); if (port == null) { Respond(500, "Invalid host-port format."); break; } if (!authHandler.AllowActiveDataConnection(port)) { Respond(500, "PORT arguments refused."); break; } dataPort = port; CreateDataSocket(false); Respond(200, GetRandomText(OK_TEXT)); break; } case "PASV": { dataPort = null; try { CreateDataSocket(true); } catch (Exception ex) { Respond(500, ex); break; } string port = FormatAddress((IPEndPoint)dataSocket.LocalEndPoint); Respond(227, String.Format("Switched to passive mode ({0})", port)); break; } case "XPWD": case "PWD": { ResultOrError <string> ret = fsHandler.GetCurrentDirectory(); if (ret.HasError) { Respond(500, ret.Error); } else { Respond(257, EscapePath(ret.Result)); } break; } case "XCWD": case "CWD": { ResultOrError <string> ret = fsHandler.ChangeDirectory(arguments); if (ret.HasError) { Respond(550, ret.Error); } else { Respond(200, GetRandomText(OK_TEXT)); } break; } case "XCUP": case "CDUP": { ResultOrError <string> ret = fsHandler.ChangeDirectory(".."); if (ret.HasError) { Respond(550, ret.Error); } else { Respond(200, GetRandomText(OK_TEXT)); } break; } case "XMKD": case "MKD": { ResultOrError <string> ret = fsHandler.CreateDirectory(arguments); if (ret.HasError) { Respond(550, ret.Error); } else { Respond(257, EscapePath(ret.Result)); } break; } case "XRMD": case "RMD": { ResultOrError <bool> ret = fsHandler.RemoveDirectory(arguments); if (ret.HasError) { Respond(550, ret.Error); } else { Respond(250, GetRandomText(OK_TEXT)); } break; } case "RETR": { ResultOrError <Stream> ret = fsHandler.ReadFile(arguments); if (ret.HasError) { Respond(550, ret.Error); break; } SendData(ret.Result); break; } case "STOR": { ResultOrError <Stream> ret = fsHandler.WriteFile(arguments); if (ret.HasError) { Respond(550, ret.Error); break; } ReceiveData(ret.Result); break; } case "DELE": { ResultOrError <bool> ret = fsHandler.RemoveFile(arguments); if (ret.HasError) { Respond(550, ret.Error); } else { Respond(250, GetRandomText(OK_TEXT)); } break; } case "RNFR": { if (arguments == null || arguments.Trim() == "") { Respond(500, "Empty path is invalid."); break; } renameFromPath = arguments; Respond(350, "Waiting for target path."); break; } case "RNTO": { if (renameFromPath == null) { Respond(503, "Use RNFR before RNTO."); break; } ResultOrError <bool> ret = fsHandler.RenameFile(renameFromPath, arguments); renameFromPath = null; if (ret.HasError) { Respond(550, ret.Error); } else { Respond(250, GetRandomText(OK_TEXT)); } break; } case "MDTM": { ResultOrError <DateTime> ret = fsHandler.GetLastModifiedTimeUtc(arguments); if (ret.HasError) { Respond(550, ret.Error); } else { Respond(213, FormatTime(EnsureUnixTime(ret.Result))); } break; } case "SIZE": { ResultOrError <long> ret = fsHandler.GetFileSize(arguments); if (ret.HasError) { Respond(550, ret.Error); } else { Respond(213, ret.Result.ToString()); } break; } case "LIST": { // apparently browsers like to pass arguments to LIST // assuming they are passed through to the UNIX ls command arguments = RemoveLsArgs(arguments); ResultOrError <FileSystemEntry[]> ret = fsHandler.ListEntries(arguments); if (ret.HasError) { Respond(500, ret.Error); break; } SendData(MakeStream(FormatDirList(ret.Result))); break; } case "STAT": { if (arguments == null || arguments.Trim() == "") { Respond(504, "Not implemented for these arguments."); break; } arguments = RemoveLsArgs(arguments); ResultOrError <FileSystemEntry[]> ret = fsHandler.ListEntries(arguments); if (ret.HasError) { Respond(500, ret.Error); break; } Respond(213, "Status:\r\n" + FormatDirList(ret.Result), true); Respond(213, "Status done."); break; } case "NLST": { // remove common arguments, we do not support any of them arguments = RemoveLsArgs(arguments); ResultOrError <FileSystemEntry[]> ret = fsHandler.ListEntries(arguments); if (ret.HasError) { Respond(500, ret.Error); break; } SendData(MakeStream(FormatNLST(ret.Result))); break; } case "NOOP": { Respond(200, GetRandomText(OK_TEXT)); break; } default: { Respond(500, "Unknown command."); break; } } }