context: // NAnt - A .NET build tool
  // Copyright (C) 2001-2002 Gerry Shaw
  //
  // This program is free software; you can redistribute it and/or modify
  // it under the terms of the GNU General Public License as published by
  // the Free Software Foundation; either version 2 of the License, or
  // (at your option) any later version.
  //
  // This program is distributed in the hope that it will be useful,
  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  // GNU General Public License for more details.
  //
  // You should have received a copy of the GNU General Public License
  // along with this program; if not, write to the Free Software
  // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  
  // Jay Turpin (jayturpin@hotmail.com)
  // Gerry Shaw (gerry_shaw@yahoo.com)
  
  using System;
  using System.Globalization;
  using System.IO;
  using System.Net;
  using System.Security.Cryptography.X509Certificates;
  
  using NAnt.Core.Attributes;
  using NAnt.Core.Types;
  using NAnt.Core.Util;
  
  namespace NAnt.Core.Tasks {
      /// <summary>
      /// Gets a particular file from a URL source.
      /// </summary>
      /// <remarks>
      ///   <para>
      ///   Options include verbose reporting and timestamp based fetches.
      ///   </para>
      ///   <para>
      ///   Currently, only HTTP and UNC protocols are supported. FTP support may 
      ///   be added when more pluggable protocols are added to the System.Net 
      ///   assembly.
      ///   </para>
      ///   <para>
      ///   The <see cref="UseTimeStamp" /> option enables you to control downloads 
      ///   so that the remote file is only fetched if newer than the local copy. 
      ///   If there is no local copy, the download always takes place. When a file 
      ///   is downloaded, the timestamp of the downloaded file is set to the remote 
      ///   timestamp.
      ///   </para>
      ///   <note>
      ///   This timestamp facility only works on downloads using the HTTP protocol.
      ///   </note>
      /// </remarks>
      /// <example>
      ///   <para>
      ///   Gets the index page of the NAnt home page, and stores it in the file 
      ///   <c>help/index.html</c> relative to the project base directory.
      ///   </para>
      ///   <code>
      ///     <![CDATA[
      /// <get src="http://nant.sourceforge.org/" dest="help/index.html" />
      ///     ]]>
      ///   </code>
      /// </example>
      /// <example>
      ///   <para>
      ///   Gets the index page of a secured web site using the given credentials, 
      ///   while connecting using the specified password-protected proxy server.
      ///   </para>
      ///   <code>
      ///     <![CDATA[
      /// <get src="http://password.protected.site/index.html" dest="secure/index.html">
      ///     <credentials username="user" password="guess" domain="mydomain" />
      ///     <proxy host="proxy.company.com" port="8080">
      ///         <credentials username="proxyuser" password="dunno" />
      ///     </proxy>
      /// </get>
      ///     ]]>
      ///   </code>
      /// </example>
      [TaskName("get")]
      public class GetTask : Task {
          #region Private Instance Fields
  
          private string _src;
          private FileInfo _destFile;
          private Proxy _proxy;
          private int _timeout = 100000;
          private bool _useTimeStamp;
          private Credential _credentials;
          private FileSet _certificates = new FileSet();
  
          #endregion Private Instance Fields
  
          #region Public Instance Properties
  
          /// <summary>
          /// The URL from which to retrieve a file.
          /// </summary>
          [TaskAttribute("src", Required=true)]
          [StringValidator(AllowEmpty=false)]
          public string Source {
              get { return _src; }
              set { _src = StringUtils.ConvertEmptyToNull(value); }
          }
  
          /// <summary>
          /// The file where to store the retrieved file.
          /// </summary>
          [TaskAttribute("dest", Required=true)]
          public FileInfo DestinationFile {
              get { return _destFile; }
              set { _destFile = value; }
          }
  
          /// <summary>
          /// The network proxy to use to access the Internet resource.
          /// </summary>
          [BuildElement("proxy")]
          public Proxy Proxy {
              get { return _proxy; }
              set { _proxy = value; }
          }
  
          /// <summary>
          /// The network credentials used for authenticating the request with 
          /// the Internet resource.
          /// </summary>
          [BuildElement("credentials")]
          public Credential Credentials {
              get { return _credentials; }
              set { _credentials = value; }
          }
  
          /// <summary>
          /// Conditionally download a file based on the timestamp of the local 
          /// copy. HTTP only. The default is <see langword="false" />.
          /// </summary>
          [TaskAttribute("usetimestamp")]
          [BooleanValidator()]
          public bool UseTimeStamp {
              get { return _useTimeStamp; }
              set { _useTimeStamp = value; }
          }
  
          /// <summary>
          /// The length of time, in milliseconds, until the request times out.
          /// The default is <c>100000</c> milliseconds.
          /// </summary>
          [TaskAttribute("timeout")]
          [Int32Validator()]
          public int Timeout {
              get { return _timeout; }
              set { _timeout = value; }
          }
  
          /// <summary>
          /// The security certificates to associate with the request.
          /// </summary>
          [BuildElement("certificates")]
          public FileSet Certificates {
              get { return _certificates; }
              set { _certificates = value; }
          }
  
          #endregion Public Instance Properties
  
          #region Override implementation of Task
  
          /// <summary>
          /// Initializes task and ensures the supplied attributes are valid.
          /// </summary>
          protected override void Initialize() {
              if (DestinationFile.Exists && (FileAttributes.ReadOnly == (File.GetAttributes(DestinationFile.FullName) & FileAttributes.ReadOnly))) {
                  throw new BuildException(string.Format("Destination file '{0}' is read-only.", 
                      DestinationFile.FullName), Location);
              }
          }
  
          /// <summary>
          /// This is where the work is done 
          /// </summary>
          protected override void ExecuteTask() {
              try {
                  //set the timestamp to the file date.
                  DateTime fileTimeStamp = new DateTime();
  
                  if (UseTimeStamp && DestinationFile.Exists) {
                      fileTimeStamp = DestinationFile.LastWriteTime;
                      Log(Level.Verbose, "Local file time stamp is {0}.", 
                          fileTimeStamp.ToString(CultureInfo.InvariantCulture));
                  }
  
                  //set up the URL connection
                  WebRequest webRequest = GetWebRequest(Source, fileTimeStamp);
                  WebResponse webResponse = webRequest.GetResponse();
  
                  Stream responseStream = null;
  
                  // Get stream
                  // try three times, then error out
                  int tryCount = 1;
  
                  while (true) {
                      try {
                          responseStream = webResponse.GetResponseStream();
                          break;
                      } catch (IOException ex) {
                          if (tryCount > 3) {
                              throw new BuildException(string.Format(CultureInfo.InvariantCulture, 
                                  ResourceUtils.GetString("NA1125"), Source, 
                                  DestinationFile.FullName), Location);
                          } else {
                              Log(Level.Warning, "Unable to open connection to '{0}' (try {1} of 3): " + ex.Message, Source, tryCount);
                          }
                      }
                  
                      // increment try count
                      tryCount++;
                  }
  
                  // open file for writing
  
gt:                 BinaryWriter destWriter = new BinaryWriter(new FileStream(