/// <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(); }