/// <summary>
        /// Telnet connections don't provide a reliable way of sending and receiving files, so we have to work around this by encoding each file with base64 and sending it separately.
        /// </summary>
        public void SendFiles(string baseTargetPath, IEnumerable <QueuedUploadedFile> files, ILongOperationCallbacks callbacks = null)
        {
            long totalSize = 0, bytesDone = 0;
            int  totalCount = 0, filesDone = 0;

            foreach (var file in files)
            {
                totalSize += file.Size;
                totalCount++;
            }

            HashSet <string> createdPaths = new HashSet <string>();

            byte[] tempBuffer             = new byte[65536];

            ManualResetEvent done = new ManualResetEvent(false);

            foreach (var file in files)
            {
                string fullPath = file.RemotePath;
                if (!fullPath.StartsWith("/"))
                {
                    fullPath = baseTargetPath + "/" + fullPath;
                }

                int    idx = fullPath.LastIndexOf('/');
                string dir = fullPath.Substring(0, idx);
                if (!createdPaths.Contains(dir))
                {
                    using (var cmd = CreateRemoteCommand($"mkdir -p \"{dir}\"", "", "", null, CommandFlags.None))
                    {
                        done.Reset();
                        cmd.CommandExited += (s, code) => done.Set();
                        cmd.Start();
                        done.WaitOne();
                    }
                }

                int exitCode = -1;
                using (var cmd = CreateRemoteCommand($"base64 -d > \"{fullPath}\"", "", "", null, CommandFlags.None))
                {
                    cmd.CommandExited += (s, code) => { done.Set(); exitCode = code ?? -1; };
                    done.Reset();
                    cmd.Start();

                    using (var stream = new MemoryStream())
                    {
                        file.CopyTo(stream, tempBuffer, file.Size);
                        cmd.SendInput(Convert.ToBase64String(stream.ToArray(), Base64FormattingOptions.InsertLineBreaks));
                    }

                    cmd.SendInput("\r\n\x04");
                    done.WaitOne();
                }

                if (exitCode == 0)
                {
                    filesDone++;
                    bytesDone += file.Size;
                    callbacks?.ReportMultiFileProgress(file.UserFriendlyName, filesDone, totalCount, bytesDone, totalSize);
                }
                else
                {
                    callbacks?.ReportFileError(fullPath, new Exception("'base64' exited with code " + exitCode));
                }
            }
        }
        public void CreateAndRetrieveTarball(string targetDirectoryToPack, string localFileOnDisk, string tarMode, ILongOperationCallbacks callbacks)
        {
            var cmd = CreateRemoteCommand($"cd \"{targetDirectoryToPack}\" && tar {tarMode} - * | base64", "", "", null, CommandFlags.None);
            ManualResetEvent done        = new ManualResetEvent(false);
            StringBuilder    builtOutput = new StringBuilder();
            var rgBase64 = new System.Text.RegularExpressions.Regex("^[A-Za-z0-9+/=\r\n]+$", System.Text.RegularExpressions.RegexOptions.Compiled);

            using (var fs = File.Create(localFileOnDisk))
            {
                cmd.CommandExited += (s, st) => done.Set();
                cmd.TextReceived  += (s, text, type) =>
                {
                    builtOutput.Append(text);
                    if (text.IndexOf('\n') != -1)
                    {
                        var str = builtOutput.ToString();
                        int lineStart = 0, lineEnd;
                        for (lineStart = 0; ; lineStart = lineEnd + 1)
                        {
                            lineEnd = str.IndexOf('\n', lineStart);
                            if (lineEnd == -1)
                            {
                                break;
                            }

                            string line = str.Substring(lineStart, lineEnd - lineStart + 1);
                            if (rgBase64.IsMatch(line))
                            {
                                var dataBlock = Convert.FromBase64String(line);
                                fs.Write(dataBlock, 0, dataBlock.Length);
                            }
                            else
                            {
                                callbacks?.ReportOutputLine(line);
                            }
                        }

                        if (lineStart >= builtOutput.Length)
                        {
                            builtOutput.Clear();
                        }
                        else
                        {
                            builtOutput.Remove(0, lineStart + 1);
                        }
                    }
                };

                cmd.Start();

                done.WaitOne();
                cmd.FlushPendingOutputEvents();

                var data = Convert.FromBase64String(builtOutput.ToString());
                fs.Write(data, 0, data.Length);
            }
        }
 /// <summary>
 /// This method is called by VisualGDB when the user steps into a source file that is not available on the Windows machine.
 /// It should download the source file to the provided temporary location so that VisualGDB can display it.
 /// Although this example does not provide an implementation for this method, you can easily implement it by running the 'base64' command and reading its output.
 /// </summary>
 public void RetrieveFile(string targetPath, string localFileOnDisk, ILongOperationCallbacks callbacks = null)
 {
     throw new NotImplementedException();
 }