예제 #1
0
 /// <summary>
 /// Not sure this works as we have not needed it.
 /// However, if password is requested by server, it will pass what was used.
 /// </summary>
 /// <param name="sender"></param>
 /// <param name="e"></param>
 protected void HandleKeyEvent(object sender, AuthenticationPromptEventArgs e)
 {
     foreach (AuthenticationPrompt prompt in e.Prompts)
     {
         if (prompt.Request.IndexOf("Password:", StringComparison.InvariantCultureIgnoreCase) != -1)
         {
             if (DataFiles.CheckPass())
             {
                 prompt.Response = Defs.PassWord;
             }
         }
     }
 }
예제 #2
0
        /// <summary>
        /// Primary Method for Command Prompt.
        /// </summary>
        public void ProptForCommands()
        {
            if (!DataFiles.PromptForHosts() || !SetupConnection())
            {
                Defs.Shutdown = true;
                return;
            }

            if (_dataTranslation == null)
            {
                DataFiles.GetTranslationData();
            }

            //change to the remote directory
            if (Defs.RemotePath.Length > 4)
            {
                SendCommand($"cd {Defs.RemotePath}", false);
                Defs.DataContent.Clear();
            }

            Console.SetCursorPosition(Console.CursorLeft, Console.CursorTop - 1);

            while (true)
            {
                if (Defs.Shutdown)
                {
                    break;
                }

                if (Defs.RequiresDirLookup)
                {
                    Regex regIP = new Regex(Defs.IP_REGEX);
                    if (regIP.IsMatch(Defs.HostName))
                    {
                        GetHostInfo();
                    }
                    if (Defs.PromptsRemoteDir == Defs.UserName)
                    {
                        Defs.PromptsRemoteDir = "~";
                    }

                    Log.Verbose($"[{Defs.UserName}@{Defs.HostNameShort} {Defs.PromptsRemoteDir}]$ ", Log.Prompt, false);
                }
                else if (!Log.IsInputRequest)
                {
                    Log.Verbose(Defs.UserServer, Log.Prompt, false);
                }

                _lastCommand = Console.ReadLine();

                if (string.IsNullOrWhiteSpace(_lastCommand))
                {
                    continue;
                }

                try
                {
                    switch (_lastCommand.ToLower().Trim())
                    {
                    case "exit":
                        Defs.Shutdown = true;
                        break;

                    case "legend":
                        General.ShowLegend();
                        break;

                    case "permissions":
                        General.ShowPermissionHelp();
                        break;

                    case "whoami":
                        Log.Verbose($"{Defs.UserName}\n", ConsoleColor.Yellow);
                        break;

                    case "cls":             //windows
                    case "clear":           //linux
                        General.ApplicationHeader(true);
                        break;

                    case "help":
                        General.DisplayHelp();
                        break;

                    case "-help":
                        SendCommand("help");
                        break;

                    default:
                        if (ExtendedCommands())
                        {
                            SendCommand(Defs.Command_Prompt_Or_Input, _lastCommand, !Log.IsInputRequest);
                        }
                        break;
                    }
                }
                catch (Exception ex)
                {
                    Log.Error($"Exception - {ex.Message}", 1);
                }
            }

            Defs.Shutdown = true;
        }
예제 #3
0
        /// <summary>
        /// Validate if the existing command is an internal commands or a user defined translation command.
        /// </summary>
        /// <returns></returns>
        private bool ExtendedCommands()
        {
            bool callExecute = false;

            string[] command = _lastCommand.Split(' ');

            if (command[0].Equals("local-dir"))
            {
                string filePath = null;

                //if path is being passed.
                if (command.Length > 1)
                {
                    filePath = command[1];
                    for (int i = 2; i < command.Length; i++)
                    {
                        filePath += command[i];
                    }
                }

                //display files and folders within selected path, set-local path if no path passed.
                Processor.ShowLocalDirectory(filePath);
            }
            else if (command[0].Equals("edit"))
            {
                //make sure file name is passed.
                if (command.Length > 1)
                {
                    //download, open in editor, then upload back to where it came from.
                    Processor.EditFile(command[1]);
                }
            }
            else if (command[0].Equals("edit-config"))
            {
                //opens config in editor, then reload config.
                DataFiles.EditConfig();
                DataFiles.LoadConfig(true);
            }
            else if (command[0].Equals("about"))
            {
                //display all information about the application and all information about the remote server your connected too.
                Log.Verbose($"-=#[ {General.AppInfo.AppName} Information ]#=-", ConsoleColor.DarkCyan);
                foreach (var field in typeof(APP_INFO).GetFields(BindingFlags.Instance |
                                                                 BindingFlags.NonPublic |
                                                                 BindingFlags.Public))
                {
                    Log.Verbose($"{General.PadString(field.Name.Trim() + ": ", 20, true)}", ConsoleColor.Cyan, false);
                    Log.Verbose(0, $"{field.GetValue(General.AppInfo)}", ConsoleColor.DarkYellow);
                }

                Log.Verbose("\n");
                Log.Verbose($"-=#[ {Defs.HostName} Information ]#=-", ConsoleColor.DarkCyan);
                _lastCommand = "whereami --more";
                ExtendedCommands();
            }
            else if (command[0].Equals("new-host"))
            {
                //disconnect from remote host.
                Disconnect();
                Defs.MissingHeaderShown = false;

                //lets go back prompt for all previous hosts or add new host.
                General.ApplicationHeader(true);
                if (DataFiles.PromptForHosts())
                {
                    //set connection based on prompts.
                    if (!SetupConnection())
                    {
                        Defs.Shutdown = true;
                    }
                }
                else
                {
                    Defs.Shutdown = true;
                }
            }
            else if (command[0].Equals("sync-date"))
            {
                //set the remote linux machine date/time to the same as your local windows machine.
                callExecute = true;

                bool   isDST          = TimeZone.CurrentTimeZone.IsDaylightSavingTime(DateTime.Now);
                string stTimeZoneName = TimeZone.CurrentTimeZone.StandardName;

                //this all works for standards in states, but there are states like Arizona that do not follow the standards.
                TimeSpan timeZoneDiff = TimeZone.CurrentTimeZone.GetUtcOffset(DateTime.Now).Add(isDST ? TimeSpan.FromHours(-1) : TimeSpan.FromHours(0));

                //build out an abbr, though might not be 100%.  This isn't something we can create out for every timezone, since Linux uses differnt names than actual timezome abbr.
                string timeZoneAbbrev = General.GetTzAbbreviation(stTimeZoneName);

                if (isDST)
                {
                    string dstTimeZoneName   = TimeZone.CurrentTimeZone.DaylightName;
                    double hours             = timeZoneDiff.TotalHours;
                    string dstTimeZoneAbbrev = General.GetTzAbbreviation(dstTimeZoneName);

                    timeZoneAbbrev += Math.Abs(hours).ToString();
                    timeZoneAbbrev += dstTimeZoneAbbrev;
                }

                //USA Only, Not sure how to handle DST with other countries as Linux doesn't seem to hold all that info correctly.
                //find /usr/share/zoneinfo/EST*
                //PST8PDT
                //MST
                //MST7MDT
                //CST6CDT
                //EST
                //EST5EDT
                //sudo timedatectl set-timezone EST
                //sudo timedatectl set-timezone EST5EDT

                //may fail as linux has some odd ways of setting DST.
                _lastCommand = $"sudo timedatectl set-timezone {timeZoneAbbrev}";
                SendCommand(_lastCommand, false);

                _lastCommand = "timedatectl set-ntp true";
                SendCommand(_lastCommand);

                string date = DateTime.Now.ToString("dd MMM yyyy HH:mm:ss");
                _lastCommand = $"date -s \"{date}\"";

                /*
                 * //I've commented this all out as even after filling out prompts, it never gets a response that it ended.
                 * string aboutServer = GetHostInfo();
                 * //tzdata is for Red Hat Linux, this includes CentOS.
                 * if (aboutServer.IndexOf("Red Hat", StringComparison.OrdinalIgnoreCase) > -1 || aboutServer.IndexOf("CentOS", StringComparison.OrdinalIgnoreCase) > -1)
                 * {
                 *  Log.Verbose("The Time Zone Database ( tzdata ) provides Red Hat Enterprise Linux (RHEL) with data that is specific to the local time zone. ... The tzdata package contains data files documenting both current and historic transitions for various time zones around the world.\n", ConsoleColor.Gray, true, false, 75);
                 *  Log.Verbose("Would you like to install tzdata on this Linux host? [y/N]: ", ConsoleColor.Yellow, false);
                 *
                 *  ConsoleKeyInfo ckiInstall = Console.ReadKey(true);
                 *  if (ckiInstall.Key == ConsoleKey.Y)
                 *  {
                 *      Log.Verbose("y");
                 *      Log.Verbose("There will be a couple of prompts that pop up, Press Y and Enter to continue the install.");
                 *      _lastCommand = "yum install tzdata";
                 *      SendCommand("(Complete!$)", _lastCommand, false);
                 *  }
                 *  else
                 *      Log.Verbose("N");
                 * }
                 * /**/
            }
            else if (command[0].Equals("recon"))
            {
                //reconnect to the server, you might of gotten disconnected from.
                General.ApplicationHeader(true);
                SetupConnection();
            }
            else if (command[0].Equals("install-ansible"))
            {
                //install PIP, and Ansible-Playbook
                InstallAnsibleWithPIP();
            }
            else if (command[0].Equals("clear-local"))
            {
                //reset local path to default.
                Defs.LocalPath = $"{General.AppInfo.AppDIR}\\Ansible";

                if (!Directory.Exists(Defs.LocalPath))
                {
                    Processor.CreateDirectory(Defs.LocalPath);
                }

                Log.Verbose($"-------------\n {Defs.LocalPath} has been set...\n", ConsoleColor.Yellow);
            }
            else if (command[0].Equals("publish"))
            {
                //uploads all files/folders from set-local and saves it to set-remote path.
                Publish();
            }
            else if (command[0].Equals("refresh"))
            {
                //downloads all files/folders from set-remote and saves it to set-local path.
                RefreshLocal();
            }
            else if (command[0].Equals("whereami"))
            {
                //display some information of server, port and curent directory.
                bool more = _lastCommand.IndexOf("--more") > -1 ? true : false;
                SendCommand("pwd", false);
                Log.Verbose($"{General.PadString("Server: ", 20, true)}", ConsoleColor.Cyan, false);
                Log.Verbose(0, $"{Defs.HostName}", ConsoleColor.DarkYellow);
                Log.Verbose($"{General.PadString("Port: ", 20, true)}", ConsoleColor.Cyan, false);
                Log.Verbose(0, $"{Defs.Port}", ConsoleColor.DarkYellow);
                Log.Verbose($"{General.PadString("Directory: ", 20, true)}", ConsoleColor.Cyan, false);
                Log.Verbose(0, $"{Defs.DataContent.Replace(Environment.NewLine, "")}", ConsoleColor.DarkYellow);
                Defs.DataContent.Clear();

                if (more)
                {
                    string[] info = GetHostInfo().Split(new[] { Environment.NewLine }, StringSplitOptions.None);
                    foreach (string s in info)
                    {
                        Log.Verbose(s, ConsoleColor.Yellow);
                    }
                }
            }
            else if (command[0].Equals("sets"))
            {
                //lets display what has been set.
                Log.Verbose($"-------------", ConsoleColor.Green);
                Log.Verbose($"Current: {Defs.LocalPath}", ConsoleColor.Yellow);
                Log.Verbose($"Change by using: 'set-local <drive>:\\<dir>\\<dir>'\n", ConsoleColor.Yellow);

                Log.Verbose($"-------------", ConsoleColor.Green);
                Log.Verbose($"Current: {Defs.RemotePath}", ConsoleColor.Yellow);
                Log.Verbose($"Change by using: 'set-remote /<dir>/<dir>'\n", ConsoleColor.Yellow);
            }
            else if (command[0].Equals("shell") || command[0].Equals("set"))
            {
                //since this is a virual UI, shell and set, holds up the command.  We don't want
                //to use these.  There might be more, but at the moment, these are a known issue.
                //eventually, we will put all commands that cause hang up, into a List<string>.
                Log.Error($"{General.AppInfo.AppName} cannot use the '{command[0]}' at this time.");
            }
            else if (command[0].Equals("get-file"))
            {
                //if file name is being passed.
                if (command.Length > 1)
                {
                    //pulls from from existing remote dir and saves to set-local path.
                    Processor.GetFile(command[1]);
                    Log.Verbose("");
                }
                else
                {
                    General.DisplayHelp("get-file");
                }
            }
            else if (command[0].Equals("send-file"))
            {
                //if file name is being passed.
                if (command.Length > 1)
                {
                    //build possible path with spaces and file name.
                    string filePath = command[1];
                    for (int i = 2; i < command.Length; i++)
                    {
                        filePath += command[i];
                    }

                    //send file to current folder.
                    string toFile = Processor.SendFile(filePath, Defs.CurrentRemotePath);
                    if (toFile.Length > 0)
                    {
                        //set permission to remote file to rwxr-xr-x
                        SendCommand($"chmod 755 {toFile}", false);
                        Defs.DataContent.Clear();
                    }
                    Log.Verbose("");
                }
                else
                {
                    General.DisplayHelp("send-file");
                }
            }
            else if (command[0].Equals("set-local"))
            {
                //if path behind set-local
                if (command.Length > 1)
                {
                    //lets build the dir, just incase there are spaces.
                    string dir = command[1];
                    for (int i = 2; i < command.Length; i++)
                    {
                        dir += $" {command[i]}";
                    }

                    //strip slash on the end, if exists.
                    if (dir.EndsWith("\\"))
                    {
                        dir = dir.Substring(0, dir.Length - 1);
                    }

                    //verify dir exists
                    if (Directory.Exists(dir))
                    {
                        //set path as requested.
                        Defs.LocalPath = dir.Trim();
                        //save to json for future access to this server.
                        DataFiles.SaveHostHistory();
                        Log.Verbose($"-------------\n Local path '{Defs.LocalPath}' has been set...\n", ConsoleColor.Yellow);
                    }
                    else
                    {
                        Log.Error($"{dir} does not exist.", 0);
                    }

                    Log.Verbose("");
                }
                else
                {
                    General.DisplayHelp("set-local");
                }
            }
            else if (command[0].Equals("set-remote"))
            {
                //if path behind set-remote
                if (command.Length > 1)
                {
                    //lets build the dir, just in case there are spaces
                    string dir = command[1];
                    for (int i = 2; i < command.Length; i++)
                    {
                        dir += command[i];
                    }

                    //strip slash on end, if exists.
                    if (dir.EndsWith("/"))
                    {
                        dir = dir.Substring(0, dir.Length - 1);
                    }

                    if (!dir.StartsWith("/"))
                    {
                        Log.Error($"{dir} must have a slash (on the front)", 0);
                    }
                    else
                    {
                        //set path as requested.
                        Defs.RemotePath = dir.Trim();
                        //save to json for future access to this server.
                        DataFiles.SaveHostHistory();
                        Log.Verbose($"-------------\n Remote path '{Defs.RemotePath}' has been set...\n", ConsoleColor.Yellow);
                    }

                    Log.Verbose("");
                }
                else
                {
                    General.DisplayHelp("set-remote");
                }
            }
            else if (command[0].Equals("cat"))
            {
                //When calling CAT w/o parameters, no prompt is coming back.
                if (command.Length > 1)
                {
                    callExecute = true;
                }
                else
                {
                    Log.Verbose("The command 'cat' requires a filename.  e.g. cat [FILE_NAME]");
                }
            }
            else if (command[0].Equals("view"))
            {
                //When calling CAT w/o parameters, no prompt is coming back.
                if (command.Length > 1)
                {
                    _lastCommand = $"cat {_lastCommand.Substring(5).Trim()}";
                    callExecute  = true;
                }
                else
                {
                    Log.Verbose("The command 'cat' requires a filename.  e.g. cat [FILE_NAME]");
                }
            }
            else
            {
                callExecute = true;

                //if calling LS, lets show the user a legend for the colors exist.
                if (command[0].Equals("dir"))
                {
                    Log.Verbose("\n Type 'legend' for more information on colors.\n", ConsoleColor.Yellow);
                }

                //if calling ansible-playbook, clear the screen, so we see results on new screen.
                if (command[0].Equals("ap"))
                {
                    General.ApplicationHeader(true);
                }

                if (_dataTranslation == null)
                {
                    _dataTranslation = DataFiles.GetTranslationData();
                }

                //lets get any possible tanslation
                DataRow[] rows = _dataTranslation.Select($"Typed='{command[0]}'");
                if (rows != null && rows.Length == 1)
                {
                    //get first row, which only should be one row.
                    DataRow dr = rows[0];

                    //see what to translate too.
                    if (DataFiles.GetField <string>(dr, "ChangeTo", out string changeTo))
                    {
                        //pull any options.
                        if (!DataFiles.GetField <string>(dr, "Options", out string options))
                        {
                            options = "";
                        }

                        //process command request.
                        TranslateUserCommand(command[0], changeTo, options);
                    }
                }
            }

            return(callExecute);
        }
예제 #4
0
        /// <summary>
        /// Setup SSH Connection to given Host.
        /// </summary>
        /// <returns></returns>
        private bool SetupConnection()
        {
            bool retVal = true;

            if (_sshClient == null || !_sshClient.IsConnected)
            {
                try
                {
                    //make sure a host exists, if not ask for one.
                    if (!DataFiles.CheckHost())
                    {
                        return(false);
                    }

                    //make sure an environment exists, if not ask for one.
                    DataFiles.CheckEnv();

                    //make sure a username exists, if not ask for one.
                    if (!DataFiles.CheckUser())
                    {
                        return(false);
                    }

                    //make sure a password exists, if not ask for one.
                    if (!DataFiles.CheckPass())
                    {
                        return(false);
                    }

                    //display welcome text for application.
                    General.ApplicationHeader(true);

                    //let the user know what we are doing.
                    Log.Verbose("\n\n Setting authentication...", ConsoleColor.Yellow);

                    //account setup..
                    KeyboardInteractiveAuthenticationMethod kauth = new KeyboardInteractiveAuthenticationMethod(Defs.UserName);
                    PasswordAuthenticationMethod            pauth = new PasswordAuthenticationMethod(Defs.UserName, Defs.PassWord);
                    kauth.AuthenticationPrompt += new EventHandler <AuthenticationPromptEventArgs>(HandleKeyEvent);

                    //setup connection
                    ConnectionInfo connectionInfo = new ConnectionInfo(Defs.HostName, Defs.Port, Defs.UserName, pauth, kauth);
                    connectionInfo.Timeout = Defs.ConnectionTimeout;

                    //pass connection data
                    _sshClient = new SshClient(connectionInfo);

                    //let the user know what we are doing.
                    Log.Verbose("Attempting to connect...", ConsoleColor.Yellow);

                    //connect to linux
                    _sshClient.Connect();

                    //let the user know what we are doing.
                    Log.Verbose("Connected, creating shell stream...", ConsoleColor.Green);

                    //lets build out a stream that will each back what we request.
                    var terminalMode = new Dictionary <TerminalModes, uint>();
                    //terminalMode.Add(TerminalModes.ECHO, 53);

                    //create shell stream
                    _shellStream = _sshClient.CreateShellStream("input", 255, 50, 400, 600, 4096, terminalMode);

                    //keep track of remote directory.  Can be useful, in case response of User@Server isn't configured to return.
                    SendCommand("pwd -P", false);
                    //it is possible that sometimes Messages come back on first connection to linux machine, before the information.
                    if (Defs.DataContent.Length > 50)
                    {
                        Defs.DataContent.Clear();
                        SendCommand("pwd -P", false);
                    }
                    //break it down for prompt.
                    string[] remote = Defs.DataContent.ToString().Replace(Environment.NewLine, "").Split('/');
                    Defs.DataContent.Clear();

                    //Don't set prompt if still showing message.
                    if (string.Join("/", remote).Trim().Length < 40 && remote.Length <= 3)
                    {
                        Defs.PromptsRemoteDir = remote[remote.Length - 1].Trim();
                    }

                    //if default Remote Path not setup, give it the current folder.  don't set remote path if still showing message.
                    if (Defs.RemotePath.Equals("/") && string.Join("/", remote).Trim().Length < 40 && remote.Length <= 3)
                    {
                        Defs.RemotePath = string.Join("/", remote).Trim();
                    }

                    //save host and user to history.
                    if (Defs.NewHost)
                    {
                        DataFiles.SaveHostHistory();
                    }

                    //let the user know what we are doing.
                    Log.Verbose("Successful Connection Established...\n", ConsoleColor.Green);

                    //set the current folder as local path
                    Directory.SetCurrentDirectory(Defs.LocalPath);
                    Console.Title = $"{Defs.ConsoleTitle}     -=[{Defs.UserName}@{Defs.HostName}]=-";

                    //clear screen and display new header
                    General.ApplicationHeader(true);

                    //let the user know what's configured.
                    Log.Verbose($"Local Path has been set to {Defs.LocalPath}...", ConsoleColor.Yellow);
                    Log.Verbose($"Remote Path has been set to {Defs.RemotePath}...\n", ConsoleColor.Yellow);
                    Log.Verbose($"Change these paths with \"set-local\" and \"set-remote\"...\n\n", ConsoleColor.Yellow);
                }
                catch (Exception ex)
                {
                    retVal = false;
                    Log.Error($"Exception - {ex.Message}", 1);
                    Log.Verbose("Press any key to continues.", ConsoleColor.White);
                    Console.ReadKey();
                    //connection didn't work right, shut it down.
                    Defs.Shutdown = true;
                }
            }

            return(retVal);
        }
예제 #5
0
        /// <summary>
        /// Display all Internal and User Translation commands.
        /// </summary>
        /// <param name="helpFor"></param>
        internal static void DisplayHelp(string helpFor = null)
        {
            if (helpFor == null)
            {
                ApplicationHeader(true);

                Log.Verbose("\n");

                Log.Verbose("      ###   ###   ###  #########  ###        ##########   ###      ", ConsoleColor.Cyan, true, true);
                Log.Verbose("    ###     ###   ###  #########  ###        ##########     ###    ", ConsoleColor.Cyan, true, true);
                Log.Verbose("  ###       ###   ###  ###        ###        ###    ###       ###  ", ConsoleColor.Cyan, true, true);
                Log.Verbose("###         #########  #########  ###        ##########         ###", ConsoleColor.Cyan, true, true);
                Log.Verbose("###         #########  #########  ###        ##########         ###", ConsoleColor.Cyan, true, true);
                Log.Verbose("  ###       ###   ###  ###        ###        ###              ###  ", ConsoleColor.Cyan, true, true);
                Log.Verbose("    ###     ###   ###  #########  #########  ###            ###    ", ConsoleColor.Cyan, true, true);
                Log.Verbose("      ###   ###   ###  #########  #########  ###          ###      ", ConsoleColor.Cyan, true, true);

                Log.Verbose("\n\n");

                Log.Verbose("Please note, all Linux commands are accepted.  These are shortcuts or", ConsoleColor.White, true, true);
                Log.Verbose("windows calls, translated into linux commands or a mixture of both.", ConsoleColor.White, true, true);

                Log.Verbose("\n\n");

                //##################################################################################################
                Log.Verbose("-=[ INTERNAL COMMANDS ]=-\n", ConsoleColor.Green, true);
            }
            else
            {
                Log.Verbose($"-=[ Example of Usage for {helpFor} ]=-\n", ConsoleColor.Cyan, true);
            }

            DataTable dtInternCmd = DataFiles.GetInternalCommands();

            foreach (DataRow dr in dtInternCmd.Select(helpFor == null ? null : $"Command='{helpFor}'", "Command"))
            {
                if (DataFiles.GetField <string>(dr, "Command", out string command))
                {
                    if (!DataFiles.GetField <string>(dr, "Description", out string descriptions))
                    {
                        descriptions = "";
                    }
                    if (!DataFiles.GetField <string>(dr, "Usage", out string usages))
                    {
                        usages = "";
                    }

                    Log.Verbose($"\t{PadString($"{command}:", 30)}", ConsoleColor.Yellow, false);

                    int dLine = 0;
                    foreach (string description in descriptions.Split(';'))
                    {
                        if (description.Trim().Length > 0)
                        {
                            if (dLine == 0)
                            {
                                Log.Verbose(0, description, ConsoleColor.Gray);
                            }
                            else
                            {
                                Log.Verbose($"\t{PadString(" ", 30)}{description}", ConsoleColor.Gray);
                            }
                            dLine++;
                        }
                    }

                    foreach (string usage in usages.Split(';'))
                    {
                        if (usage.Trim().Length > 0)
                        {
                            Log.Verbose($"\t{PadString(" ", 30)}e.g. {usage}", ConsoleColor.Gray);
                        }
                    }

                    Log.Verbose($"\t{new string('-', 75)}", ConsoleColor.Gray);
                }
            }

            //extra \r\n
            Console.WriteLine("");

            if (helpFor == null)
            {
                //##################################################################################################
                Log.Verbose("-=[ USER COMMANDS ]=-", ConsoleColor.Green, true);
                Log.Verbose($"Can be edited or added to here:", ConsoleColor.Cyan, true);
                Log.Verbose(5, $"{DataFiles.DataFile}\n", ConsoleColor.Cyan, true);

                DataTable dtTransData = DataFiles.GetTranslationData();
                foreach (DataRow dr in dtTransData.Select(null, "Typed"))
                {
                    if (DataFiles.GetField <string>(dr, "Typed", out string command))
                    {
                        if (DataFiles.GetField <string>(dr, "ChangeTo", out string changeTo))
                        {
                            if (!DataFiles.GetField <string>(dr, "Options", out string options))
                            {
                                options = "";
                            }
                            if (!DataFiles.GetField <string>(dr, "Description", out string description))
                            {
                                description = "";
                            }
                            if (!DataFiles.GetField <string>(dr, "Usage", out string usages))
                            {
                                usages = "";
                            }

                            Log.Verbose($"\t{PadString($"{command}:", 30)}", ConsoleColor.Yellow, false);
                            Log.Verbose(0, description, ConsoleColor.Gray);
                            Log.Verbose($"\t{PadString(" ", 30)}Translates To: {changeTo} {options}", ConsoleColor.Gray);

                            foreach (string usage in usages.Split(';'))
                            {
                                if (usage.Trim().Length > 0)
                                {
                                    Log.Verbose($"\t{PadString(" ", 30)}e.g. {usage}", ConsoleColor.Gray);
                                }
                            }

                            Log.Verbose($"\t{new string('-', 75)}", ConsoleColor.Gray);
                        }
                    }
                }

                //extra \r\n
                Console.WriteLine("");
            }
        }