using System; #if !TUNING using System.Collections.Generic; #endif using System.IO; #if !TUNING using System.Linq; #endif using Renci.SshNet.Common; using System.Globalization; using Renci.SshNet.Sftp.Responses; using System.Text; namespace Renci.SshNet.Sftp { internal abstract class SftpMessage : SshData { public static SftpMessage Load(uint protocolVersion, byte[] data, Encoding encoding) { #if TUNING var messageType = (SftpMessageTypes) data[4]; // skip packet length bytes #else var messageType = (SftpMessageTypes)data.FirstOrDefault(); #endif return Load(protocolVersion, data, messageType, encoding); } protected override int ZeroReaderIndex { get { #if TUNING // 4 bytes for the length of the SFTP data // 1 byte for the SFTP message type return 5; #else return 1; #endif } } #if TUNING /// /// Gets the size of the message in bytes. /// /// /// The size of the messages in bytes. /// protected override int BufferCapacity { get { return ZeroReaderIndex; } } #endif public abstract SftpMessageTypes SftpMessageType { get; } protected override void LoadData() { } protected override void SaveData() { Write((byte) SftpMessageType); } #if TUNING /// /// Writes the current message to the specified . /// /// The to write the message to. protected override void WriteBytes(SshDataStream stream) { const int sizeOfDataLengthBytes = 4; var startPosition = stream.Position; // skip 4 bytes for the length of the SFTP message data stream.Seek(sizeOfDataLengthBytes, SeekOrigin.Current); // write the SFTP message data to the stream base.WriteBytes(stream); // save where we were positioned when we finished writing the SSH message data var endPosition = stream.Position; // determine the length of the SSH message data var dataLength = endPosition - startPosition - sizeOfDataLengthBytes; // write the length of the SFTP message where we were positioned before we started // writing the SFTP message data stream.Position = startPosition; stream.Write((uint) dataLength); // move back to we were positioned when we finished writing the SFTP message data stream.Position = endPosition; } #endif protected SftpFileAttributes ReadAttributes() { #if TUNING return SftpFileAttributes.FromBytes(DataStream); #else var flag = ReadUInt32(); long size = -1; var userId = -1; var groupId = -1; uint permissions = 0; var accessTime = DateTime.MinValue; var modifyTime = DateTime.MinValue; IDictionary extensions = null; if ((flag & 0x00000001) == 0x00000001) // SSH_FILEXFER_ATTR_SIZE { size = (long)ReadUInt64(); } if ((flag & 0x00000002) == 0x00000002) // SSH_FILEXFER_ATTR_UIDGID { userId = (int)ReadUInt32(); groupId = (int)ReadUInt32(); } if ((flag & 0x00000004) == 0x00000004) // SSH_FILEXFER_ATTR_PERMISSIONS { permissions = ReadUInt32(); } if ((flag & 0x00000008) == 0x00000008) // SSH_FILEXFER_ATTR_ACMODTIME { var time = ReadUInt32(); accessTime = DateTime.FromFileTime((time + 11644473600) * 10000000); time = ReadUInt32(); modifyTime = DateTime.FromFileTime((time + 11644473600) * 10000000); } if ((flag & 0x80000000) == 0x80000000) // SSH_FILEXFER_ATTR_ACMODTIME { var extendedCount = ReadUInt32(); extensions = ReadExtensionPair(); } var attributes = new SftpFileAttributes(accessTime, modifyTime, size, userId, groupId, permissions, extensions); return attributes; #endif } #if !TUNING protected void Write(SftpFileAttributes attributes) { if (attributes == null) { Write((uint)0); return; } UInt32 flag = 0; if (attributes.IsSizeChanged && attributes.IsRegularFile) { flag |= 0x00000001; } if (attributes.IsUserIdChanged|| attributes.IsGroupIdChanged) { flag |= 0x00000002; } if (attributes.IsPermissionsChanged) { flag |= 0x00000004; } if (attributes.IsLastAccessTimeChanged || attributes.IsLastWriteTimeChanged) { flag |= 0x00000008; } if (attributes.IsExtensionsChanged) { flag |= 0x80000000; } Write(flag); if (attributes.IsSizeChanged && attributes.IsRegularFile) { Write((UInt64)attributes.Size); } if (attributes.IsUserIdChanged|| attributes.IsGroupIdChanged) { Write((UInt32)attributes.UserId); Write((UInt32)attributes.GroupId); } if (attributes.IsPermissionsChanged) { Write(attributes.Permissions); } if (attributes.IsLastAccessTimeChanged || attributes.IsLastWriteTimeChanged) { var time = (uint)(attributes.LastAccessTime.ToFileTime() / 10000000 - 11644473600); Write(time); time = (uint)(attributes.LastWriteTime.ToFileTime() / 10000000 - 11644473600); Write(time); } if (attributes.IsExtensionsChanged) { Write(attributes.Extensions); } } #endif private static SftpMessage Load(uint protocolVersion, byte[] data, SftpMessageTypes messageType, Encoding encoding) { SftpMessage message; switch (messageType) { case SftpMessageTypes.Version: message = new SftpVersionResponse(); break; case SftpMessageTypes.Status: message = new SftpStatusResponse(protocolVersion); break; case SftpMessageTypes.Data: message = new SftpDataResponse(protocolVersion); break; case SftpMessageTypes.Handle: message = new SftpHandleResponse(protocolVersion); break; case SftpMessageTypes.Name: message = new SftpNameResponse(protocolVersion, encoding); break; case SftpMessageTypes.Attrs: message = new SftpAttrsResponse(protocolVersion); break; case SftpMessageTypes.ExtendedReply: message = new SftpExtendedReplyResponse(protocolVersion); break; default: throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "Message type '{0}' is not supported.", messageType)); } #if TUNING message.Load(data); #else message.LoadBytes(data); message.ResetReader(); message.LoadData(); #endif return message; } public override string ToString() { return string.Format(CultureInfo.CurrentCulture, "SFTP Message : {0}", SftpMessageType); } } }