diff --git a/PUP/Authentication.cs b/PUP/Authentication.cs index ff02ac4..08ae4d8 100644 --- a/PUP/Authentication.cs +++ b/PUP/Authentication.cs @@ -38,6 +38,18 @@ namespace IFS return _accounts.Values.ToList(); } + public static UserToken GetUser(string userName) + { + if (_accounts.ContainsKey(userName)) + { + return _accounts[userName]; + } + else + { + return null; + } + } + public static UserToken Authenticate(string userName, string password) { // diff --git a/PUP/BSP/BSPManager.cs b/PUP/BSP/BSPManager.cs index 877bb23..8074c98 100644 --- a/PUP/BSP/BSPManager.cs +++ b/PUP/BSP/BSPManager.cs @@ -152,7 +152,7 @@ namespace IFS.BSP case PupType.Abort: { string abortMessage = Helpers.ArrayToString(p.Contents); - Log.Write(LogType.Warning, LogComponent.RTP, String.Format("BSP aborted, message: '{0}'", abortMessage)); + Log.Write(LogType.Warning, LogComponent.RTP, String.Format("BSP aborted, message from client: '{0}'", abortMessage)); DestroyChannel(channel); } @@ -257,7 +257,8 @@ namespace IFS.BSP } else { - // TODO: send back "server full" repsonse of some sort. + // Send an Abort with an informative message. + channel.SendAbort("IFS Server full, try again later."); } } diff --git a/PUP/Conf/ifs.cfg b/PUP/Conf/ifs.cfg index 99fce23..d2a81b5 100644 --- a/PUP/Conf/ifs.cfg +++ b/PUP/Conf/ifs.cfg @@ -6,7 +6,7 @@ # Debug settings -LogTypes = All +LogTypes = Error LogComponents = All # Normal configuration @@ -15,3 +15,5 @@ CopyDiskRoot = c:\ifs\copydisk BootRoot = c:\ifs\boot InterfaceType = RAW InterfaceName = Ethernet 2 +ServerNetwork = 1 +ServerHost = 1 diff --git a/PUP/Configuration.cs b/PUP/Configuration.cs index f0b940f..b117c61 100644 --- a/PUP/Configuration.cs +++ b/PUP/Configuration.cs @@ -63,6 +63,16 @@ namespace IFS /// public static readonly string InterfaceName; + /// + /// The network that this server lives on + /// + public static readonly int ServerNetwork; + + /// + /// The host number for this server. + /// + public static readonly int ServerHost; + /// /// The root directory for the FTP file store. /// diff --git a/PUP/Console/Console.cs b/PUP/Console/Console.cs new file mode 100644 index 0000000..7fa584a --- /dev/null +++ b/PUP/Console/Console.cs @@ -0,0 +1,581 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Text; +using System.IO; + +namespace IFS.IfsConsole +{ + /// + /// Defines a node in the debug command tree. + /// + public class ConsoleCommand + { + public ConsoleCommand(string name, String description, String usage, MethodInfo method) + { + Name = name.Trim().ToLower(); + Description = description; + Usage = usage; + Methods = new List(4); + + if (method != null) + { + Methods.Add(method); + } + + SubCommands = new List(); + } + + public string Name; + public string Description; + public string Usage; + public List Methods; + public List SubCommands; + + public override string ToString() + { + if (this.Methods.Count == 0) + { + return String.Format("{0}... ({1})", this.Name, this.SubCommands.Count); + } + else + { + return this.Name; + } + } + + public void AddSubNode(List words, MethodInfo method) + { + // We should never hit this case. + if (words.Count == 0) + { + throw new InvalidOperationException("Out of words building command node."); + } + + // Check the root to see if a node for the first incoming word has already been added + ConsoleCommand subNode = FindSubNodeByName(words[0]); + + if (subNode == null) + { + // No, it has not -- create one and add it now. + subNode = new ConsoleCommand(words[0], null, null, null); + this.SubCommands.Add(subNode); + + if (words.Count == 1) + { + // This is the last stop -- set the method and be done with it now. + subNode.Methods.Add(method); + + // early return. + return; + } + } + else + { + // The node already exists, we will be adding a subnode, hopefully. + if (words.Count == 1) + { + // + // If we're on the last word at this point then this is an overloaded command. + // Check that we don't have any other commands with this number of arguments. + // + int argCount = method.GetParameters().Length; + foreach (MethodInfo info in subNode.Methods) + { + if (info.GetParameters().Length == argCount) + { + throw new InvalidOperationException("Duplicate overload for console command"); + } + } + + // + // We're ok. Add it to the method list. + // + subNode.Methods.Add(method); + + // and return early. + return; + } + } + + // We have more words to go. + words.RemoveAt(0); + subNode.AddSubNode(words, method); + + } + + public ConsoleCommand FindSubNodeByName(string name) + { + ConsoleCommand found = null; + + foreach (ConsoleCommand sub in SubCommands) + { + if (sub.Name == name) + { + found = sub; + break; + } + } + + return found; + } + } + + public class ConsoleExecutor + { + private ConsoleExecutor() + { + BuildCommandTree(); + + _consolePrompt = new ConsolePrompt(_commandRoot); + } + + public static ConsoleExecutor Instance + { + get + { + if (_instance == null) + { + _instance = new ConsoleExecutor(); + } + + return _instance; + } + } + + public void Run() + { + while (true) + { + try + { + // Get the command string from the prompt. + string command = _consolePrompt.Prompt().Trim(); + + if (command != String.Empty) + { + ExecuteLine(command); + } + } + catch (Exception e) + { + Console.WriteLine(e.Message); + } + } + } + + private void ExecuteLine(string line) + { + // Comments start with "#" + if (line.StartsWith("#")) + { + // Do nothing, ignore. + } + else + { + string[] args = null; + List methods = GetMethodsFromCommandString(line, out args); + + if (methods == null) + { + // Not a command. + Console.WriteLine("Invalid command."); + + } + else + { + InvokeConsoleMethod(methods, args); + } + } + } + + private void InvokeConsoleMethod(List methods, string[] args) + { + MethodInfo method = null; + + // + // Find the method that matches the arg count we were passed + // (i.e. handle overloaded commands) + // + foreach (MethodInfo m in methods) + { + ParameterInfo[] paramInfo = m.GetParameters(); + + if (args == null && paramInfo.Length == 0 || + paramInfo.Length == args.Length) + { + // found a match + method = m; + break; + } + } + + if (method == null) + { + // invalid argument count. + // todo: display usage? + throw new ArgumentException(String.Format("Invalid argument count to command.")); + } + + ParameterInfo[] parameterInfo = method.GetParameters(); + object[] invokeParams; + + if (args == null) + { + invokeParams = null; + } + else + { + invokeParams = new object[parameterInfo.Length]; + } + + for (int i = 0; i < parameterInfo.Length; i++) + { + ParameterInfo p = parameterInfo[i]; + + if (p.ParameterType.IsEnum) + { + // + // This is an enumeration type. + // See if we can find an enumerant that matches the argument. + // + FieldInfo[] fields = p.ParameterType.GetFields(); + + foreach (FieldInfo f in fields) + { + if (!f.IsSpecialName && args[i].ToLower() == f.Name.ToLower()) + { + invokeParams[i] = f.GetRawConstantValue(); + } + } + + if (invokeParams[i] == null) + { + // no match, provide possible values + StringBuilder sb = new StringBuilder(String.Format("Invalid value for parameter {0}. Possible values are:", i)); + + foreach (FieldInfo f in fields) + { + if (!f.IsSpecialName) + { + sb.AppendFormat("{0} ", f.Name); + } + } + + sb.AppendLine(); + + throw new ArgumentException(sb.ToString()); + } + + } + else if (p.ParameterType.IsArray) + { + // + // If a function takes an array type, i should do something here, yeah. + // + } + else + { + // must be something more normal... + if (p.ParameterType == typeof(uint)) + { + invokeParams[i] = TryParseUint(args[i]); + } + else if (p.ParameterType == typeof(ushort)) + { + invokeParams[i] = TryParseUshort(args[i]); + } + else if (p.ParameterType == typeof(string)) + { + invokeParams[i] = args[i]; + } + else if (p.ParameterType == typeof(char)) + { + invokeParams[i] = (char)args[i][0]; + } + else + { + throw new ArgumentException(String.Format("Unhandled type for parameter {0}, type {1}", i, p.ParameterType)); + } + } + } + + // + // If we've made it THIS far, then we were able to parse all the commands into what they should be. + // Invoke the method on the static instance exposed by the class. + // + object instance = GetInstanceFromMethod(method); + + method.Invoke(instance, invokeParams); + + } + + private object GetInstanceFromMethod(MethodInfo method) + { + Type instanceType = method.DeclaringType; + PropertyInfo property = instanceType.GetProperty("Instance"); + return property.GetValue(null, null); + } + + enum ParseState + { + NonWhiteSpace = 0, + WhiteSpace = 1, + QuotedString = 2, + } + + private List SplitArgs(string commandString) + { + // We split on whitespace and specially handle quoted strings (quoted strings count as a single arg) + // + List args = new List(); + + commandString = commandString.Trim(); + + StringBuilder sb = new StringBuilder(); + + ParseState state = ParseState.NonWhiteSpace; + + foreach(char c in commandString) + { + switch (state) + { + case ParseState.NonWhiteSpace: + if (char.IsWhiteSpace(c)) + { + // End of token + args.Add(sb.ToString()); + sb.Clear(); + state = ParseState.WhiteSpace; + } + else if (c == '\"') + { + // Start of quoted string + state = ParseState.QuotedString; + } + else + { + // Character in token + sb.Append(c); + } + break; + + case ParseState.WhiteSpace: + if (!char.IsWhiteSpace(c)) + { + // Start of new token + if (c != '\"') + { + sb.Append(c); + state = ParseState.NonWhiteSpace; + } + else + { + // Start of quoted string + state = ParseState.QuotedString; + } + } + break; + + case ParseState.QuotedString: + if (c == '\"') + { + // End of quoted string. + args.Add(sb.ToString()); + sb.Clear(); + state = ParseState.WhiteSpace; + } + else + { + // Character in quoted string + sb.Append(c); + } + break; + } + } + + if (sb.Length > 0) + { + // Add the last token to the args list + args.Add(sb.ToString()); + } + + return args; + } + + private List GetMethodsFromCommandString(string command, out string[] args) + { + args = null; + + List cmdArgs = SplitArgs(command); + + ConsoleCommand current = _commandRoot; + int commandIndex = 0; + + while (true) + { + // If this node has an executor, then we're done + // (We assume that the tree is correctly built and that only + // leaves have executors) + if (current.Methods.Count > 0) + { + break; + } + + if (commandIndex > cmdArgs.Count - 1) + { + // Out of args! + return null; + } + + // Otherwise we continue down the tree. + current = current.FindSubNodeByName(cmdArgs[commandIndex]); + commandIndex++; + + if (current == null) + { + // If the node was not found, then the command is invalid. + return null; + } + } + + //Now current should point to the command with the executor + //and commandIndex should point to the first argument to the command. + + cmdArgs.RemoveRange(0, commandIndex); + + args = cmdArgs.ToArray(); + return current.Methods; + } + + + + private static uint TryParseUint(string arg) + { + uint result = 0; + bool hexadecimal = false; + + //if args starts with a "0x" it's hex + //otherwise assume decimal + if (arg.StartsWith("0x")) + { + hexadecimal = true; + + //strip the "0x" + arg = arg.Remove(0, 2); + } + + try + { + result = uint.Parse(arg, hexadecimal ? System.Globalization.NumberStyles.HexNumber : System.Globalization.NumberStyles.Integer); + } + catch + { + Console.WriteLine("{0} was not a valid 32-bit decimal or hexadecimal constant.", arg); + throw; + } + + return result; + } + + private static ushort TryParseUshort(string arg) + { + ushort result = 0; + bool hexadecimal = false; + + //if args starts with a "0x" it's hex + //otherwise assume decimal + if (arg.StartsWith("0x")) + { + hexadecimal = true; + + //strip the "0x" + arg = arg.Remove(0, 2); + } + + try + { + result = ushort.Parse(arg, hexadecimal ? System.Globalization.NumberStyles.HexNumber : System.Globalization.NumberStyles.Integer); + } + catch + { + Console.WriteLine("{0} was not a valid 16-bit decimal or hexadecimal constant.", arg); + throw; + } + + return result; + } + + /// + /// Builds the debugger command tree. + /// + private void BuildCommandTree() + { + // Build the flat list which will be built into the tree, by walking + // the classes that provide the methods + _commandList = new List(); + + Type[] commandTypes = { + typeof(ConsoleCommands), + typeof(ConsoleExecutor), + }; + + foreach (Type type in commandTypes) + { + foreach (MethodInfo info in type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)) + { + object[] attribs = info.GetCustomAttributes(typeof(ConsoleFunction), true); + + if (attribs.Length > 1) + { + throw new InvalidOperationException(String.Format("More than one ConsoleFunction attribute set on {0}", info.Name)); + } + else if (attribs.Length == 1) + { + // we have a debugger attribute set on this method + // this cast should always succeed given that we're filtering for this type above. + ConsoleFunction function = (ConsoleFunction)attribs[0]; + + ConsoleCommand newCommand = new ConsoleCommand(function.CommandName, function.Description, function.Usage, info); + + _commandList.Add(newCommand); + } + } + } + + // Now actually build the command tree from the above list! + _commandRoot = new ConsoleCommand("Root", null, null, null); + + foreach (ConsoleCommand c in _commandList) + { + string[] commandWords = c.Name.Split(' '); + + // This is kind of ugly, we know that at this point every command built above have only + // one method. When building the tree, overloaded commands may end up with more than one. + _commandRoot.AddSubNode(new List(commandWords), c.Methods[0]); + } + } + + [ConsoleFunction("show commands", "Shows debugger commands and their descriptions.")] + private void ShowCommands() + { + foreach (ConsoleCommand cmd in _commandList) + { + if (!string.IsNullOrEmpty(cmd.Usage)) + { + Console.WriteLine("{0} - {1}\n {2}\n", cmd.Name, cmd.Description, cmd.Usage); + } + else + { + Console.WriteLine("{0} - {1}", cmd.Name, cmd.Description); + } + } + } + + private ConsolePrompt _consolePrompt; + private ConsoleCommand _commandRoot; + private List _commandList; + + private static ConsoleExecutor _instance; + } +} diff --git a/PUP/Console/ConsoleAttributes.cs b/PUP/Console/ConsoleAttributes.cs new file mode 100644 index 0000000..ca8393c --- /dev/null +++ b/PUP/Console/ConsoleAttributes.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace IFS.IfsConsole +{ + public class ConsoleFunction : Attribute + { + public ConsoleFunction(string commandName) + { + _commandName = commandName; + _usage = ""; + } + + public ConsoleFunction(string commandName, string description) + { + _commandName = commandName; + _description = description; + } + + public ConsoleFunction(string commandName, string description, string usage) + { + _commandName = commandName; + _description = description; + _usage = usage; + } + + public string CommandName + { + get { return _commandName; } + } + + public string Usage + { + get { return _usage; } + } + + public string Description + { + get { return _description; } + } + + private string _commandName; + private string _description; + private string _usage; + } +} diff --git a/PUP/Console/ConsoleCommands.cs b/PUP/Console/ConsoleCommands.cs new file mode 100644 index 0000000..66135c2 --- /dev/null +++ b/PUP/Console/ConsoleCommands.cs @@ -0,0 +1,96 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace IFS.IfsConsole +{ + public class ConsoleCommands + { + private ConsoleCommands() + { + + } + + public static ConsoleCommands Instance + { + get + { + if (_instance == null) + { + _instance = new ConsoleCommands(); + } + + return _instance; + } + } + + [ConsoleFunction("show users", "Displays the current user database")] + private void ShowUsers() + { + foreach(UserToken user in Authentication.EnumerateUsers()) + { + Console.WriteLine("{0}: ({1}) - {2},{3}", user.UserName, user.Privileges, user.FullName, user.HomeDirectory); + } + } + + [ConsoleFunction("show user", "Displays information for the specified user")] + private void ShowUser(string username) + { + UserToken user = Authentication.GetUser(username); + + if (user == null) + { + Console.WriteLine("User '{0}' does not exist.", username); + } + else + { + Console.WriteLine("{0}: ({1}) - {2},{3}", user.UserName, user.Privileges, user.FullName, user.HomeDirectory); + } + } + + [ConsoleFunction("set password", "Sets the password for the specified user")] + private void SetPassword(string username, string newPassword) + { + UserToken user = Authentication.GetUser(username); + + if (user == null) + { + Console.WriteLine("User '{0}' does not exist.", username); + } + else + { + Authentication.SetPassword(username, newPassword); + } + } + + [ConsoleFunction("add user", "Adds a new user", " [User|Admin] ")] + private void AddUser(string username, string newPassword, string privileges, string fullName, string homeDir) + { + IFSPrivileges privs = IFSPrivileges.ReadOnly; + + switch(privileges.ToLowerInvariant()) + { + case "user": + privs = IFSPrivileges.ReadOnly; + break; + + case "admin": + privs = IFSPrivileges.ReadWrite; + break; + } + + if (Authentication.AddUser(username, newPassword, fullName, homeDir, privs)) + { + Console.WriteLine("User added."); + } + else + { + Console.WriteLine("User already exists."); + } + } + + private static ConsoleCommands _instance; + } +} diff --git a/PUP/Console/ConsolePrompt.cs b/PUP/Console/ConsolePrompt.cs new file mode 100644 index 0000000..e8e7286 --- /dev/null +++ b/PUP/Console/ConsolePrompt.cs @@ -0,0 +1,456 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace IFS.IfsConsole +{ + public class ConsolePrompt + { + + public ConsolePrompt(ConsoleCommand root) + { + _commandTree = root; + _commandHistory = new List(64); + _historyIndex = 0; + } + + /// + /// Runs a nifty interactive debugger prompt. + /// + public string Prompt() + { + DisplayPrompt(); + ClearInput(); + UpdateOrigin(); + + bool entryDone = false; + + while (!entryDone) + { + UpdateDisplay(); + + // Read one keystroke from the console... + ConsoleKeyInfo key = Console.ReadKey(true); + + //Parse special chars... + switch (key.Key) + { + case ConsoleKey.Escape: //Clear input + ClearInput(); + break; + + case ConsoleKey.Backspace: // Delete last char + DeleteCharAtCursor(true /* backspace */); + break; + + case ConsoleKey.Delete: //Delete character at cursor + DeleteCharAtCursor(false /* delete */); + break; + + case ConsoleKey.LeftArrow: + MoveLeft(); + break; + + case ConsoleKey.RightArrow: + MoveRight(); + break; + + case ConsoleKey.UpArrow: + HistoryPrev(); + break; + + case ConsoleKey.DownArrow: + HistoryNext(); + break; + + case ConsoleKey.Home: + MoveToBeginning(); + break; + + case ConsoleKey.End: + MoveToEnd(); + break; + + case ConsoleKey.Tab: + DoCompletion(false /* silent */); + break; + + case ConsoleKey.Enter: + DoCompletion(true /* silent */); + UpdateDisplay(); + CRLF(); + entryDone = true; + break; + + case ConsoleKey.Spacebar: + if (!_input.EndsWith(" ") && + DoCompletion(true /* silent */)) + { + UpdateDisplay(); + } + else + { + InsertChar(key.KeyChar); + } + break; + + default: + // Not a special key, just insert it if it's deemed printable. + if (char.IsLetterOrDigit(key.KeyChar) || + char.IsPunctuation(key.KeyChar) || + char.IsSymbol(key.KeyChar) || + char.IsWhiteSpace(key.KeyChar)) + { + InsertChar(key.KeyChar); + } + break; + + } + } + + // Done. Add to history if input is non-empty + if (_input != string.Empty) + { + _commandHistory.Add(_input); + HistoryIndex = _commandHistory.Count - 1; + } + + return _input; + } + + private void DeleteCharAtCursor(bool backspace) + { + if (_input.Length == 0) + { + //nothing to delete, bail. + return; + } + + if (backspace) + { + if (TextPosition == 0) + { + // We's at the beginning of the input, + // can't backspace from here. + return; + } + else + { + // remove 1 char at the position before the cursor. + // and move the cursor back one char + _input = _input.Remove(TextPosition - 1, 1); + + TextPosition--; + } + } + else + { + if (TextPosition == _input.Length) + { + // At the end of input, can't delete a char from here + return; + } + else + { + // remove one char at the current cursor pos. + // do not move the cursor + _input = _input.Remove(TextPosition, 1); + } + } + } + + private void DisplayPrompt() + { + Console.Write(">"); + } + + private void UpdateDisplay() + { + //if the current input string is shorter than the last, then we need to erase a few chars at the end. + string clear = String.Empty; + + if (_input.Length < _lastInputLength) + { + StringBuilder sb = new StringBuilder(_lastInputLength - _input.Length); + for (int i = 0; i < _lastInputLength - _input.Length; i++) + { + sb.Append(' '); + } + + clear = sb.ToString(); + } + + int column = ((_textPosition + _originColumn) % Console.BufferWidth); + int row = ((_textPosition + _originColumn) / Console.BufferWidth) + _originRow; + + // Move cursor to origin to draw string + Console.CursorLeft = _originColumn; + Console.CursorTop = _originRow; + Console.Write(_input + clear); + + // Move cursor to text position to draw cursor + Console.CursorLeft = column; + Console.CursorTop = row; + Console.CursorVisible = true; + + _lastInputLength = _input.Length; + } + + private void MoveLeft() + { + TextPosition--; + } + + private void MoveRight() + { + TextPosition++; + } + + private void HistoryPrev() + { + + HistoryIndex--; + if (HistoryIndex < _commandHistory.Count) + { + _input = _commandHistory[HistoryIndex]; + TextPosition = _input.Length; + } + } + + private void HistoryNext() + { + HistoryIndex++; + if (HistoryIndex < _commandHistory.Count) + { + _input = _commandHistory[HistoryIndex]; + TextPosition = _input.Length; + } + } + + private void MoveToBeginning() + { + TextPosition = 0; + } + + private void MoveToEnd() + { + TextPosition = _input.Length; + } + + private void ClearInput() + { + _input = String.Empty; + TextPosition = 0; + } + + private void InsertChar(char c) + { + _input = _input.Insert(TextPosition, c.ToString()); + TextPosition++; + } + + private void CRLF() + { + Console.WriteLine(); + } + + private bool DoCompletion(bool silent) + { + // This code should probably move to another class, but hey, I'm lazy. + + // Take the current input and see if it matches anything in the command tree + string[] tokens = _input.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); + + // Save off the current cursor row; this is an ugly-ish hack to detect whether the match process + // output anything. If the cursor row changes then we'll need to move the prompt + int oldRow = Console.CursorTop; + + string matchString = FuzzyMatch(_commandTree, new List(tokens), silent); + + bool changed = false; + + if (matchString != String.Empty) + { + changed = _input.Trim().ToLower() != matchString.Trim().ToLower(); + + _input = matchString; + TextPosition = _input.Length; + } + + if (!silent && oldRow != Console.CursorTop) + { + DisplayPrompt(); + UpdateOrigin(); + } + + return changed; + } + + private string FuzzyMatch(ConsoleCommand root, List tokens, bool silent) + { + if (tokens.Count == 0) + { + if (!silent) + { + // If there are no tokens, just show the completion for the root. + PrintCompletions(root.SubCommands); + } + return String.Empty; + } + + ConsoleCommand match = null; + bool exactMatch = false; + + // Search for exact matches. If we find one it's guaranteed to be unique + // so we can follow that node. + foreach (ConsoleCommand c in root.SubCommands) + { + if (c.Name.ToLower() == tokens[0].ToLower()) + { + match = c; + exactMatch = true; + break; + } + } + + if (match == null) + { + // No exact match. Try a substring match. + // If we have an unambiguous match then we can complete it automatically. + // If the match is ambiguous, display possible completions and return String.Empty. + List completions = new List(); + + foreach (ConsoleCommand c in root.SubCommands) + { + if (c.Name.StartsWith(tokens[0], StringComparison.InvariantCultureIgnoreCase)) + { + completions.Add(c); + } + } + + if (completions.Count == 1) + { + // unambiguous match. use it. + match = completions[0]; + } + else if (completions.Count > 1) + { + // ambiguous match. display possible completions. + if (!silent) + { + PrintCompletions(completions); + } + } + } + + if (match == null) + { + // If we reach this point then no matches are available. return the tokens we have... + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < tokens.Count; i++) + { + if (i < tokens.Count - 1) + { + sb.AppendFormat("{0} ", tokens[i]); + } + else + { + sb.AppendFormat("{0}", tokens[i]); + } + } + + return sb.ToString(); + } + else + { + // A match was found + tokens.RemoveAt(0); + + string subMatch = String.Empty; + + if (tokens.Count > 0) + { + subMatch = FuzzyMatch(match, tokens, silent); + } + else if (exactMatch) + { + if (!silent && match.SubCommands.Count > 0) + { + // Just show the completions for this node. + PrintCompletions(match.SubCommands); + } + } + + if (subMatch == String.Empty) + { + return String.Format("{0} ", match.Name); + } + else + { + return String.Format("{0} {1}", match.Name, subMatch); + } + } + } + + private void PrintCompletions(List completions) + { + // Just print all available completions at this node + Console.WriteLine(); + Console.WriteLine("Possible completions are:"); + foreach (ConsoleCommand c in completions) + { + Console.Write("{0}\t", c); + } + Console.WriteLine(); + } + + private void UpdateOrigin() + { + _originRow = Console.CursorTop; + _originColumn = Console.CursorLeft; + } + + private int TextPosition + { + get + { + return _textPosition; + } + + set + { + // Clip input between 0 and the length of input (+1, to allow adding text at end) + _textPosition = Math.Max(0, value); + _textPosition = Math.Min(_textPosition, _input.Length); + } + } + + private int HistoryIndex + { + get + { + return _historyIndex; + } + + set + { + _historyIndex = Math.Min(_commandHistory.Count - 1, value); + _historyIndex = Math.Max(0, _historyIndex); + + } + } + + private ConsoleCommand _commandTree; + + + private int _originRow; + private int _originColumn; + + private string _input; + private int _textPosition; + private int _lastInputLength; + + private List _commandHistory; + private int _historyIndex; + } +} diff --git a/PUP/DirectoryServices.cs b/PUP/DirectoryServices.cs index 1049383..ce932dd 100644 --- a/PUP/DirectoryServices.cs +++ b/PUP/DirectoryServices.cs @@ -37,7 +37,7 @@ namespace IFS // Get our host address; for now just hardcode it. // TODO: need to define config files, etc. - _localHost = new HostAddress(1, 1); + _localHost = new HostAddress((byte)Configuration.ServerNetwork, (byte)Configuration.ServerHost); // Load in hosts table from hosts file. LoadHostTable(); diff --git a/PUP/Entrypoint.cs b/PUP/Entrypoint.cs index be7ed01..afe8c0e 100644 --- a/PUP/Entrypoint.cs +++ b/PUP/Entrypoint.cs @@ -1,6 +1,7 @@ using IFS.Boot; using IFS.CopyDisk; using IFS.FTP; +using IFS.IfsConsole; using IFS.Transport; using PcapDotNet.Core; using PcapDotNet.Core.Extensions; @@ -96,20 +97,10 @@ namespace IFS } private static void RunCommandPrompt() - { - List users = Authentication.EnumerateUsers(); - - Authentication.SetPassword(users[0].UserName, "hamdinger"); - - UserToken user = Authentication.Authenticate(users[0].UserName, "hamdinger"); - - while (true) - { - Console.Write(">>>"); - string command = Console.ReadLine(); - } + { + ConsoleExecutor.Instance.Run(); } - private static BreathOfLife _breathOfLifeServer; + private static BreathOfLife _breathOfLifeServer; } } diff --git a/PUP/IFS.csproj b/PUP/IFS.csproj index 78768c3..aa6000d 100644 --- a/PUP/IFS.csproj +++ b/PUP/IFS.csproj @@ -79,6 +79,10 @@ + + + +