| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572 | using System;using System.Collections.Generic;using System.IO;using System.Linq;using System.Globalization;using System.Text;using Renci.SshNet.Common;namespace Renci.SshNet.Sftp{    /// <summary>    /// Contains SFTP file attributes.    /// </summary>    public class SftpFileAttributes    {        #region Bitmask constats        private const UInt32 S_IFMT = 0xF000; //  bitmask for the file type bitfields        private const UInt32 S_IFSOCK = 0xC000; //	socket        private const UInt32 S_IFLNK = 0xA000; //	symbolic link        private const UInt32 S_IFREG = 0x8000; //	regular file        private const UInt32 S_IFBLK = 0x6000; //	block device        private const UInt32 S_IFDIR = 0x4000; //	directory        private const UInt32 S_IFCHR = 0x2000; //	character device        private const UInt32 S_IFIFO = 0x1000; //	FIFO        private const UInt32 S_ISUID = 0x0800; //	set UID bit        private const UInt32 S_ISGID = 0x0400; //	set-group-ID bit (see below)        private const UInt32 S_ISVTX = 0x0200; //	sticky bit (see below)        private const UInt32 S_IRUSR = 0x0100; //	owner has read permission        private const UInt32 S_IWUSR = 0x0080; //	owner has write permission        private const UInt32 S_IXUSR = 0x0040; //	owner has execute permission        private const UInt32 S_IRGRP = 0x0020; //	group has read permission        private const UInt32 S_IWGRP = 0x0010; //	group has write permission        private const UInt32 S_IXGRP = 0x0008; //	group has execute permission        private const UInt32 S_IROTH = 0x0004; //	others have read permission        private const UInt32 S_IWOTH = 0x0002; //	others have write permission        private const UInt32 S_IXOTH = 0x0001; //	others have execute permission        #endregion        private bool _isBitFiledsBitSet;        private bool _isUIDBitSet;        private bool _isGroupIDBitSet;        private bool _isStickyBitSet;        private readonly DateTime _originalLastAccessTime;        private readonly DateTime _originalLastWriteTime;        private readonly long _originalSize;        private readonly int _originalUserId;        private readonly int _originalGroupId;        private readonly uint _originalPermissions;        private readonly IDictionary<string, string> _originalExtensions;        internal bool IsLastAccessTimeChanged        {            get { return _originalLastAccessTime != LastAccessTime; }        }        internal bool IsLastWriteTimeChanged        {            get { return _originalLastWriteTime != LastWriteTime; }        }        internal bool IsSizeChanged        {            get { return _originalSize != Size; }        }        internal bool IsUserIdChanged        {            get { return _originalUserId != UserId; }        }        internal bool IsGroupIdChanged        {            get { return _originalGroupId != GroupId; }        }        internal bool IsPermissionsChanged        {            get { return _originalPermissions != Permissions; }        }        internal bool IsExtensionsChanged        {            get { return _originalExtensions != null && Extensions != null && !_originalExtensions.SequenceEqual(Extensions); }        }        /// <summary>        /// Gets or sets the time the current file or directory was last accessed.        /// </summary>        /// <value>        /// The time that the current file or directory was last accessed.        /// </value>        public DateTime LastAccessTime { get; set; }        /// <summary>        /// Gets or sets the time when the current file or directory was last written to.        /// </summary>        /// <value>        /// The time the current file was last written.        /// </value>        public DateTime LastWriteTime { get; set; }        /// <summary>        /// Gets or sets the size, in bytes, of the current file.        /// </summary>        /// <value>        /// The size of the current file in bytes.        /// </value>        public long Size { get; set; }        /// <summary>        /// Gets or sets file user id.        /// </summary>        /// <value>        /// File user id.        /// </value>        public int UserId { get; set; }        /// <summary>        /// Gets or sets file group id.        /// </summary>        /// <value>        /// File group id.        /// </value>        public int GroupId { get; set; }        /// <summary>        /// Gets a value indicating whether file represents a socket.        /// </summary>        /// <value>        ///   <c>true</c> if file represents a socket; otherwise, <c>false</c>.        /// </value>        public bool IsSocket { get; private set; }        /// <summary>        /// Gets a value indicating whether file represents a symbolic link.        /// </summary>        /// <value>        /// 	<c>true</c> if file represents a symbolic link; otherwise, <c>false</c>.        /// </value>        public bool IsSymbolicLink { get; private set; }        /// <summary>        /// Gets a value indicating whether file represents a regular file.        /// </summary>        /// <value>        /// 	<c>true</c> if file represents a regular file; otherwise, <c>false</c>.        /// </value>        public bool IsRegularFile { get; private set; }        /// <summary>        /// Gets a value indicating whether file represents a block device.        /// </summary>        /// <value>        /// 	<c>true</c> if file represents a block device; otherwise, <c>false</c>.        /// </value>        public bool IsBlockDevice { get; private set; }        /// <summary>        /// Gets a value indicating whether file represents a directory.        /// </summary>        /// <value>        /// 	<c>true</c> if file represents a directory; otherwise, <c>false</c>.        /// </value>        public bool IsDirectory { get; private set; }        /// <summary>        /// Gets a value indicating whether file represents a character device.        /// </summary>        /// <value>        /// 	<c>true</c> if file represents a character device; otherwise, <c>false</c>.        /// </value>        public bool IsCharacterDevice { get; private set; }        /// <summary>        /// Gets a value indicating whether file represents a named pipe.        /// </summary>        /// <value>        /// 	<c>true</c> if file represents a named pipe; otherwise, <c>false</c>.        /// </value>        public bool IsNamedPipe { get; private set; }        /// <summary>        /// Gets a value indicating whether the owner can read from this file.        /// </summary>        /// <value>        ///   <c>true</c> if owner can read from this file; otherwise, <c>false</c>.        /// </value>        public bool OwnerCanRead { get; set; }        /// <summary>        /// Gets a value indicating whether the owner can write into this file.        /// </summary>        /// <value>        ///   <c>true</c> if owner can write into this file; otherwise, <c>false</c>.        /// </value>        public bool OwnerCanWrite { get; set; }        /// <summary>        /// Gets a value indicating whether the owner can execute this file.        /// </summary>        /// <value>        ///   <c>true</c> if owner can execute this file; otherwise, <c>false</c>.        /// </value>        public bool OwnerCanExecute { get; set; }        /// <summary>        /// Gets a value indicating whether the group members can read from this file.        /// </summary>        /// <value>        ///   <c>true</c> if group members can read from this file; otherwise, <c>false</c>.        /// </value>        public bool GroupCanRead { get; set; }        /// <summary>        /// Gets a value indicating whether the group members can write into this file.        /// </summary>        /// <value>        ///   <c>true</c> if group members can write into this file; otherwise, <c>false</c>.        /// </value>        public bool GroupCanWrite { get; set; }        /// <summary>        /// Gets a value indicating whether the group members can execute this file.        /// </summary>        /// <value>        ///   <c>true</c> if group members can execute this file; otherwise, <c>false</c>.        /// </value>        public bool GroupCanExecute { get; set; }        /// <summary>        /// Gets a value indicating whether the others can read from this file.        /// </summary>        /// <value>        ///   <c>true</c> if others can read from this file; otherwise, <c>false</c>.        /// </value>        public bool OthersCanRead { get; set; }        /// <summary>        /// Gets a value indicating whether the others can write into this file.        /// </summary>        /// <value>        ///   <c>true</c> if others can write into this file; otherwise, <c>false</c>.        /// </value>        public bool OthersCanWrite { get; set; }        /// <summary>        /// Gets a value indicating whether the others can execute this file.        /// </summary>        /// <value>        ///   <c>true</c> if others can execute this file; otherwise, <c>false</c>.        /// </value>        public bool OthersCanExecute { get; set; }        /// <summary>        /// Gets or sets the extensions.        /// </summary>        /// <value>        /// The extensions.        /// </value>        public IDictionary<string, string> Extensions { get; private set; }        internal uint Permissions        {            get            {                uint permission = 0;                if (_isBitFiledsBitSet)                    permission = permission | S_IFMT;                if (IsSocket)                    permission = permission | S_IFSOCK;                if (IsSymbolicLink)                    permission = permission | S_IFLNK;                if (IsRegularFile)                    permission = permission | S_IFREG;                if (IsBlockDevice)                    permission = permission | S_IFBLK;                if (IsDirectory)                    permission = permission | S_IFDIR;                if (IsCharacterDevice)                    permission = permission | S_IFCHR;                if (IsNamedPipe)                    permission = permission | S_IFIFO;                if (_isUIDBitSet)                    permission = permission | S_ISUID;                if (_isGroupIDBitSet)                    permission = permission | S_ISGID;                if (_isStickyBitSet)                    permission = permission | S_ISVTX;                if (OwnerCanRead)                    permission = permission | S_IRUSR;                if (OwnerCanWrite)                    permission = permission | S_IWUSR;                if (OwnerCanExecute)                    permission = permission | S_IXUSR;                if (GroupCanRead)                    permission = permission | S_IRGRP;                if (GroupCanWrite)                    permission = permission | S_IWGRP;                if (GroupCanExecute)                    permission = permission | S_IXGRP;                if (OthersCanRead)                    permission = permission | S_IROTH;                if (OthersCanWrite)                    permission = permission | S_IWOTH;                if (OthersCanExecute)                    permission = permission | S_IXOTH;                return permission;            }            private set            {                _isBitFiledsBitSet = ((value & S_IFMT) == S_IFMT);                IsSocket = ((value & S_IFSOCK) == S_IFSOCK);                IsSymbolicLink = ((value & S_IFLNK) == S_IFLNK);                IsRegularFile = ((value & S_IFREG) == S_IFREG);                IsBlockDevice = ((value & S_IFBLK) == S_IFBLK);                IsDirectory = ((value & S_IFDIR) == S_IFDIR);                IsCharacterDevice = ((value & S_IFCHR) == S_IFCHR);                IsNamedPipe = ((value & S_IFIFO) == S_IFIFO);                _isUIDBitSet = ((value & S_ISUID) == S_ISUID);                _isGroupIDBitSet = ((value & S_ISGID) == S_ISGID);                _isStickyBitSet = ((value & S_ISVTX) == S_ISVTX);                OwnerCanRead = ((value & S_IRUSR) == S_IRUSR);                OwnerCanWrite = ((value & S_IWUSR) == S_IWUSR);                OwnerCanExecute = ((value & S_IXUSR) == S_IXUSR);                GroupCanRead = ((value & S_IRGRP) == S_IRGRP);                GroupCanWrite = ((value & S_IWGRP) == S_IWGRP);                GroupCanExecute = ((value & S_IXGRP) == S_IXGRP);                OthersCanRead = ((value & S_IROTH) == S_IROTH);                OthersCanWrite = ((value & S_IWOTH) == S_IWOTH);                OthersCanExecute = ((value & S_IXOTH) == S_IXOTH);            }        }        private SftpFileAttributes()        {        }        internal SftpFileAttributes(DateTime lastAccessTime, DateTime lastWriteTime, long size, int userId, int groupId, uint permissions, IDictionary<string, string> extensions)        {            LastAccessTime = _originalLastAccessTime = lastAccessTime;            LastWriteTime = _originalLastWriteTime = lastWriteTime;            Size = _originalSize = size;            UserId = _originalUserId = userId;            GroupId = _originalGroupId = groupId;            Permissions = _originalPermissions = permissions;            Extensions = _originalExtensions = extensions;        }        /// <summary>        /// Sets the permissions.        /// </summary>        /// <param name="mode">The mode.</param>        public void SetPermissions(short mode)        {            if (mode < 0 || mode > 999)            {                throw new ArgumentOutOfRangeException("mode");            }            var modeBytes = mode.ToString(CultureInfo.InvariantCulture).PadLeft(3, '0').ToArray();            var permission = (modeBytes[0] & 0x0F) * 8 * 8 + (modeBytes[1] & 0x0F) * 8 + (modeBytes[2] & 0x0F);            OwnerCanRead = (permission & S_IRUSR) == S_IRUSR;            OwnerCanWrite = (permission & S_IWUSR) == S_IWUSR;            OwnerCanExecute = (permission & S_IXUSR) == S_IXUSR;            GroupCanRead = (permission & S_IRGRP) == S_IRGRP;            GroupCanWrite = (permission & S_IWGRP) == S_IWGRP;            GroupCanExecute = (permission & S_IXGRP) == S_IXGRP;            OthersCanRead = (permission & S_IROTH) == S_IROTH;            OthersCanWrite = (permission & S_IWOTH) == S_IWOTH;            OthersCanExecute = (permission & S_IXOTH) == S_IXOTH;        }        public byte[] GetBytes()        {            var stream = new SshDataStream(4);            uint flag = 0;            if (IsSizeChanged && IsRegularFile)            {                flag |= 0x00000001;            }            if (IsUserIdChanged || IsGroupIdChanged)            {                flag |= 0x00000002;            }            if (IsPermissionsChanged)            {                flag |= 0x00000004;            }            if (IsLastAccessTimeChanged || IsLastWriteTimeChanged)            {                flag |= 0x00000008;            }            if (IsExtensionsChanged)            {                flag |= 0x80000000;            }            stream.Write(flag);            if (IsSizeChanged && IsRegularFile)            {                stream.Write((ulong) Size);            }            if (IsUserIdChanged || IsGroupIdChanged)            {                stream.Write((uint) UserId);                stream.Write((uint) GroupId);            }            if (IsPermissionsChanged)            {                stream.Write(Permissions);            }            if (IsLastAccessTimeChanged || IsLastWriteTimeChanged)            {                var time = (uint)(LastAccessTime.ToFileTime() / 10000000 - 11644473600);                stream.Write(time);                time = (uint)(LastWriteTime.ToFileTime() / 10000000 - 11644473600);                stream.Write(time);            }            if (IsExtensionsChanged)            {                foreach (var item in Extensions)                {                    // TODO: we write as ASCII but read as UTF8 !!!                    stream.Write(item.Key, SshData.Ascii);                    stream.Write(item.Value, SshData.Ascii);                }            }            return stream.ToArray();        }        internal static readonly SftpFileAttributes Empty = new SftpFileAttributes();        internal static SftpFileAttributes FromBytes(SshDataStream stream)        {            var flag = stream.ReadUInt32();            long size = -1;            var userId = -1;            var groupId = -1;            uint permissions = 0;            var accessTime = DateTime.MinValue;            var modifyTime = DateTime.MinValue;            IDictionary<string, string> extensions = null;            if ((flag & 0x00000001) == 0x00000001)   //  SSH_FILEXFER_ATTR_SIZE            {                size = (long) stream.ReadUInt64();            }            if ((flag & 0x00000002) == 0x00000002)   //  SSH_FILEXFER_ATTR_UIDGID            {                userId = (int) stream.ReadUInt32();                groupId = (int) stream.ReadUInt32();            }            if ((flag & 0x00000004) == 0x00000004)   //  SSH_FILEXFER_ATTR_PERMISSIONS            {                permissions = stream.ReadUInt32();            }            if ((flag & 0x00000008) == 0x00000008)   //  SSH_FILEXFER_ATTR_ACMODTIME            {                var time = stream.ReadUInt32();                accessTime = DateTime.FromFileTime((time + 11644473600) * 10000000);                time = stream.ReadUInt32();                modifyTime = DateTime.FromFileTime((time + 11644473600) * 10000000);            }            if ((flag & 0x80000000) == 0x80000000)   //  SSH_FILEXFER_ATTR_EXTENDED            {                var extendedCount = (int) stream.ReadUInt32();                extensions = new Dictionary<string, string>(extendedCount);                for (var i = 0; i < extendedCount; i++)                {                    var extensionName = stream.ReadString(SshData.Utf8);                    var extensionData = stream.ReadString(SshData.Utf8);                    extensions.Add(extensionName, extensionData);                }            }            return new SftpFileAttributes(accessTime, modifyTime, size, userId, groupId, permissions, extensions);        }        internal static SftpFileAttributes FromBytes(byte[] buffer)        {            using (var stream = new SshDataStream(buffer))            {                return FromBytes(stream);            }        }    }}
 |