Пример #1
0
        private void DoIntegrityCheck(FtpsRequest request, Stream stream, long restartPosition)
        {
            // get the file path from the request argument
            string path = request.Arguments[0];
            long startPos = restartPosition;
            long endPos = stream.Length;

            string streamHash = ComputeHash(_autoHashAlgorithm, stream, startPos);
            string serverHash = GetHash(_autoHashAlgorithm, path, startPos, endPos);

            streamHash = streamHash.ToLower();
            serverHash = serverHash.ToLower();

            // string compare the dataHash to the server hash value and see if they are the same
            if (serverHash.IndexOf(streamHash, 0) == -1)
                throw new FtpsHashingException(String.Format("File integrity check failed.  The destination hash value '{0}' for the file '{1}' did not match the data transfer hash value '{2}'.", serverHash, path, streamHash));

            //// string compare the dataHash to the server hash value and see if they are the same
            //if (String.Compare(streamHash, serverHash, StringComparison.InvariantCultureIgnoreCase) != 0)
            //    throw new FtpHashingException(String.Format("File integrity check failed.  The destination hash value '{0}' for the file '{1}' did not match the data transfer hash value '{2}'.", serverHash, path, streamHash));
        }
Пример #2
0
        internal void OpenDataConnAndTransferData(TransferDirection direction, FtpsRequest request, Stream data, long restartPosition)
        {
            try
            {
                // create a thread to begin the porocess of opening a data connection to the remote server
                OpenDataConn();

                //  check for a restart position 
                if (restartPosition > 0)
                {
                    // instruct the server to restart file transfer at the same position where the output stream left off
                    SendRequest(new FtpsRequest(_encode, FtpsCmd.Rest, restartPosition.ToString(CultureInfo.InvariantCulture)));
                    
                    // set the data stream to the same position as the server
                    data.Position = restartPosition;
                }

                // send the data transfer command that requires a separate data connection to be established to transmit data
                SendRequest(request);

                // wait for the data connection thread to signal back that a connection has been established
                WaitForDataConn();

                // test to see if the data connection failed to be established sometime the active connection fails due to security settings on the ftp server
                if (_dataConn == null)
                    throw new FtpsDataConnectionException("Unable to establish a data connection to the destination.  The destination may have refused the connection.");

                // create the data stream object - handles creation of SslStream and DeflateStream objects as well
                Stream conn = _dataConn.GetStream();

                // test to see if we need to enable ssl/tls explicit mode
                if (_securityProtocol != FtpsSecurityProtocol.None)
                    conn = CreateSslStream(conn);
                
                // test to see if we need to enable compression by using the DeflateStream
                if (_isCompressionEnabled)
                    conn = CreateZlibStream(direction, conn);

                // based on the direction of the data transfer we need to handle the input and output streams
                switch (direction)
                {
                    case TransferDirection.ToClient:
                        TransferBytes(conn, data, _maxDownloadSpeed * 1024);
                        break;
                    case TransferDirection.ToServer:
                        TransferBytes(data, conn, _maxUploadSpeed * 1024);
                        break;
                }

            }
            finally
            {
                // reset the transfer size used to calc % completed
                SetTransferSize(-1);
                // attempt to close the data connection 
                CloseDataConn();
            }

            // if no errors occurred and this is not a quoted command then we will wait for the server to send a closing connection message
            WaitForHappyCodes(FtpsResponseCode.ClosingDataConnection);

            // automatic integrity check with file transfer
            if (_autoHashAlgorithm != HashingAlgorithm.None 
                && request.IsFileTransfer)
                DoIntegrityCheck(request, data, restartPosition);
        }
Пример #3
0
 internal string TransferText(FtpsRequest request)
 {
     Stream output = new MemoryStream();
     TransferData(TransferDirection.ToClient, request, output);
     output.Position = 0;
     StreamReader reader = new StreamReader(output, _encode);
     return reader.ReadToEnd();
 }
Пример #4
0
 internal void TransferData(TransferDirection direction, FtpsRequest request, Stream data)
 {
     TransferData(direction, request, data, 0);
 }
Пример #5
0
        internal void TransferData(TransferDirection direction, FtpsRequest request, Stream data, long restartPosition)
        {
            if (_commandConn == null || _commandConn.Connected == false)
                throw new FtpsConnectionClosedException("Connection is closed.");

            if (request == null)
                throw new ArgumentNullException("request", "value is required");

            if (data == null)
                throw new ArgumentNullException("data", "value is required");

            switch (direction)
            {
                case TransferDirection.ToClient:
                    if (!data.CanWrite)
                        throw new FtpsDataTransferException("Data transfer error.  Data conn does not allow write operation.");
                    break;
                case TransferDirection.ToServer:
                    if (!data.CanRead)
                        throw new FtpsDataTransferException("Data transfer error.  Data conn does not allow read operation.");
                    break;
            }

            // if this is a restart then the data stream must support seeking
            if (restartPosition > 0 && !data.CanSeek)
                throw new FtpsDataTransferException("Data transfer restart error.  Data conn does not allow seek operation.");

            OpenDataConnAndTransferData(direction, request, data, restartPosition);
        }
Пример #6
0
        /// <summary>
        /// Send a FTP command request to the server.
        /// </summary>
        /// <param name="request"></param>
        internal void SendRequest(FtpsRequest request)
        {

            if (_commandConn == null || _commandConn.Connected == false)
                throw new FtpsConnectionClosedException("Connection is closed.");

            // clear out any responses that might have been pending from a previous
            // failed operation
            DontWaitForHappyCodes();

            if (ClientRequest != null)
                ClientRequest(this, new FtpsRequestEventArgs(request));

            byte[] buffer = request.GetBytes();

            // write request to debug out
            Debug.Write("S: " + _encode.GetString(buffer));

            try
            {
                // sychronize on streamWriteLock object to prevent non-blocking exception errors
                lock (_cmdStreamWriteLock)
                {
                    _commandStream.Write(buffer, 0, buffer.Length);
                }
            }
            catch (IOException ex)
            {
                throw new FtpsConnectionBrokenException("Connection is broken.  Failed to send command.", ex);
            }

            // most commands will have happy codes but the quote() command does not 
            if (request.HasHappyCodes)
            {
                WaitForHappyCodes(request.GetHappyCodes());
            }
            else
            {
                // when there are no happy codes given the we have to give the server some time to response
                // since we really don't know what response is the correct one
                if (request.Command != FtpsCmd.Quit)
                    Thread.Sleep(2000);
                DontWaitForHappyCodes();
            }
        }
        /// <summary>
        /// Constructor for FtpRequestEventArgs.
        /// </summary>
        /// <param name="request">An FtpRequest object.</param>
 		public FtpsRequestEventArgs(FtpsRequest request)
		{
            _request = request;
		}
Пример #8
0
 /// <summary>
 /// Constructor for FtpRequestEventArgs.
 /// </summary>
 /// <param name="request">An FtpRequest object.</param>
 public FtpsRequestEventArgs(FtpsRequest request)
 {
     _request = request;
 }
Пример #9
0
        /// <summary>
        /// Retrieves a remote file from the FTP server and writes the data to a local stream object
        /// specfied in the outStream parameter.
        /// </summary> 
        /// <param name="remotePath">A path and/or file name to the remote file.</param>
        /// <param name="outStream">An output stream object used to stream the remote file to the local machine.</param>
        /// <param name="resume">A true/false value to indicate if the file download needs to be restarted due to a previous partial download.</param>
        /// <remarks>
        /// If the remote path is a file name then the file is downloaded from the FTP server current working directory.  Otherwise a fully qualified
        /// path for the remote file may be specified.  The output stream must be writeable and can be any stream object.  Finally, the restart parameter
        /// is used to send a restart command to the FTP server.  The FTP server is instructed to restart the download process at the last position of
        /// of the output stream.  Not all FTP servers support the restart command.  If the FTP server does not support the restart (REST) command,
        /// an FtpException error is thrown.
        /// Note that some FTP servers will not accept a full path.  On those systems you must navigate to
        /// the directory you wish to get the file using with the ChangeDirectory() or ChangeDirectoryMultiPath()
        /// method.
        /// </remarks>
        /// <seealso cref="GetFileAsync(string, string, FileAction)"/>
        /// <seealso cref="PutFile(string, string, FileAction)"/>
        /// <seealso cref="PutFileAsync(string, string, FileAction)"/>
        /// <seealso cref="MoveFile"/>
        /// <seealso cref="FxpCopy"/>
        /// <seealso cref="FxpCopyAsync"/>
        public void GetFile(string remotePath, Stream outStream, bool resume)
        {
            if (remotePath == null)
                throw new ArgumentNullException("remotePath");
            if (remotePath.Length == 0)
                throw new ArgumentException("must contain a value", "remotePath");
            if (outStream == null)
                throw new ArgumentNullException("outStream");
            if (outStream.CanWrite == false)
                throw new ArgumentException("must be writable.  The CanWrite property must return the value 'true'.", "outStream");

            // if the transfer event is being subscribed or this is a resume action then get the file size
            long remoteSize = -1;
            if (IsTranferProgressEventSet() || resume)
            {
                // try to get the remote file size 
                TryGetFileSize(remotePath, out remoteSize);
                // set the transfer size so we can do some calc on percentage completed
                // in the TransferBytes() method of the base class
                SetTransferSize(remoteSize);
            }

            FtpsRequest request = new FtpsRequest(base.Encoding, FtpsCmd.Retr, remotePath);

            if (resume)
            {
                //  make sure we have a valid file size
                if (remoteSize == -1)
                    throw new FtpsException("unable to determine file size for resume transfer");

                // if the files are the same size then there is nothing to transfer
                if (outStream.Length == remoteSize)
                    return;

                TransferData(TransferDirection.ToClient, request, outStream, outStream.Length - 1);
            }
            else
            {
                TransferData(TransferDirection.ToClient, request, outStream);
            }
        }
Пример #10
0
        /// <summary>
        /// Retrieves a remote file from the FTP server and writes the data to a local file
        /// specfied in the localPath parameter.
        /// </summary>
        /// <remarks>
        /// Note that some FTP servers will not accept a full path.  On those systems you must navigate to
        /// the directory you wish to get the file using with the ChangeDirectory() or ChangeDirectoryMultiPath()
        /// method.
        /// </remarks>
        /// <param name="remotePath">A path and/or file name to the remote file.</param>
        /// <param name="localPath">A fully qualified local path to a file on the local machine.</param>
        /// <param name="action">The type of action to take.</param>
        /// <seealso cref="GetFileAsync(string, string, FileAction)"/>
        /// <seealso cref="PutFile(string, string, FileAction)"/>
        /// <seealso cref="PutFileAsync(string, string, FileAction)"/>
        /// <seealso cref="MoveFile"/>
        /// <seealso cref="FxpCopy"/>
        /// <seealso cref="FxpCopyAsync"/>
        public void GetFile(string remotePath, string localPath, FileAction action)
        {
            if (remotePath == null)
                throw new ArgumentNullException("remotePath");
            if (remotePath.Length == 0)
                throw new ArgumentException("must contain a value", "remotePath");
            if (localPath == null)
                throw new ArgumentNullException("localPath");
            if (localPath.Length == 0)
                throw new ArgumentException("must contain a value", "localPath");
            if (action == FileAction.None)
                throw new ArgumentOutOfRangeException("action", "must contain a value other than 'Unknown'");

            // if the transfer event is being subscribed or this is a resume action then get the file size
            long remoteSize = -1;
            if (IsTranferProgressEventSet() || action == FileAction.Resume)
            {
                // try to get the remote file size 
                TryGetFileSize(remotePath, out remoteSize);
                // set the transfer size so we can do some calc on percentage completed
                // in the TransferBytes() method of the base class
                SetTransferSize(remoteSize);
            }

            localPath = CorrectLocalPath(localPath);

            WriteToLog(String.Format("Action='GetFile';Status='TransferBegin';LocalPath='{0}';RemotePath='{1}';FileAction='{2}'", localPath, remotePath, action.ToString()));

            FtpsRequest request = new FtpsRequest(base.Encoding, FtpsCmd.Retr, remotePath);

            try
            {
                switch (action)
                {
                    case FileAction.CreateNew:
                        // create a file stream to stream the file locally to disk that only creates the file if it does not already exist
                        using (Stream localFile = File.Open(localPath, FileMode.CreateNew))
                        {
                            TransferData(TransferDirection.ToClient, request, localFile);
                        }
                        break;
                    
                    case FileAction.Create:
                        // create a file stream to stream the file locally to disk
                        using (Stream localFile = File.Open(localPath, FileMode.Create))
                        {
                            TransferData(TransferDirection.ToClient, request, localFile);
                        }
                        break;
                    case FileAction.CreateOrAppend:
                        // open the local file
                        using (Stream localFile = File.Open(localPath, FileMode.OpenOrCreate))
                        {
                            // set the file position to the end so that any new data will be appended                        
                            localFile.Position = localFile.Length;
                            TransferData(TransferDirection.ToClient, request, localFile);
                        }
                        break;
                    case FileAction.Resume:
                        if (!base.Features.Contains(FtpsCmd.Rest, "STREAM"))
                            throw new FtpsCommandNotSupportedException("Cannot resume file transfer." ,"REST STREAM");

                        using (Stream localFile = File.Open(localPath, FileMode.Open))
                        {
                            //  make sure we have a valid file size
                            if (remoteSize == -1)
                                throw new FtpsException("unable to determine file size for resume transfer");

                            // if the files are the same size then there is nothing to transfer
                            if (localFile.Length == remoteSize)
                                return;

                            TransferData(TransferDirection.ToClient, request, localFile, localFile.Length - 1);
                        }
                        break;
                    case FileAction.ResumeOrCreate:
                        if (File.Exists(localPath) && (new FileInfo(localPath)).Length > 0)
                            GetFile(remotePath, localPath, FileAction.Resume);
                        else
                            GetFile(remotePath, localPath, FileAction.Create);
                        break;
                }
            }
            catch (Exception ex)
            {
                WriteToLog(String.Format("Action='GetFile';Status='TransferError';LocalPath='{0}';RemotePath='{1}';FileAction='{1}';ErrorMessage='{2}", localPath, remotePath, action.ToString(), ex.Message));
                throw new FtpsException(String.Format("An unexpected exception occurred while retrieving file '{0}'.", remotePath), base.LastResponse, ex); 
            }

            WriteToLog(String.Format("Action='GetFile';Status='TransferSuccess';LocalPath='{0}';RemotePath='{1}';FileAction='{1}'", localPath, remotePath, action.ToString()));
        }