OSDN Git Service

na-get-lib,ファイルダウンロードで attachment;filename= のようにセミコロン後にスペースがない場合の対応
[applistation/AppliStation.git] / na-get-lib / NaGet.SubCommands.SubTask / DownloadSubTask.cs
index e24ad4f..74cd2a9 100644 (file)
@@ -49,11 +49,21 @@ namespace NaGet.SubCommands.SubTask
                public System.Net.Cache.RequestCacheLevel CacheLevel = System.Net.Cache.RequestCacheLevel.NoCacheNoStore;
                
                /// <summary>
+               /// ダウンロード時に downloadedFileName に改名するか否か。
+               /// </summary>
+               protected bool enableChangeFileName = false;
+               
+               /// <summary>
                /// キャンセルが呼ばれたか否か。
                /// </summary>
                private bool cancelCalled = false;
                
                /// <summary>
+               /// ダウンロード中のファイル名につける接尾辞
+               /// </summary>
+               private static readonly string PartialFilePostfix = ".part";
+               
+               /// <summary>
                /// コンストラクタ
                /// </summary>
                /// <param name="url">ダウンロード先URL</param>
@@ -70,6 +80,11 @@ namespace NaGet.SubCommands.SubTask
                        this.downloadedFileName = null;
                }
                
+               public DownloadSubTask(string url, string filepath, IWebProxy proxy)
+                       : this(new Uri(url), filepath, proxy)
+               {
+               }
+               
                /// <summary>
                /// コンストラクタ
                /// </summary>
@@ -79,6 +94,11 @@ namespace NaGet.SubCommands.SubTask
                        : this(url, filepath, NaGet.Env.WebProxy)
                {
                }
+               
+               public DownloadSubTask(string url, string filepath)
+                       : this(new Uri(url), filepath, NaGet.Env.WebProxy)
+               {
+               }
        
                /// <summary>
                /// ダウンロード時にHTTPヘッダなどから取得した本来のファイル名
@@ -88,6 +108,21 @@ namespace NaGet.SubCommands.SubTask
                }
                
                /// <summary>
+               /// 保存先ファイル名を本来のファイル名に変えるか。
+               /// </summary>
+               public bool EnableChangeFileName {
+                       get { return enableChangeFileName; }
+                       set { enableChangeFileName = value; }
+               }
+               
+               /// <summary>
+               /// 保存ファイル。
+               /// </summary>
+               public string Filepath {
+                       get { return filepath; }
+               }
+               
+               /// <summary>
                /// キャンセル可能
                /// </summary>
                public override bool Cancelable {
@@ -133,6 +168,15 @@ namespace NaGet.SubCommands.SubTask
                                runHandleCancelTrigger();
                                
                                runPostprocess();
+                               
+                               RaiseTaskSetEvent(TaskEventType.COMPLETED, "ダウンロード終了", 100);
+                       } catch (System.Net.WebException e) {
+                               if (System.Net.NetworkInformation.NetworkInterface.GetIsNetworkAvailable()) {
+                                       RaiseTaskSetEvent(TaskEventType.WARNING, "ネットワークに接続されていません。", -1);
+                               } else {
+                                       RaiseTaskSetEvent(TaskEventType.WARNING, "ネットワークに接続できませんでした。ネットワークが切断されているか、ファイアウォールによって遮断された可能性があります。", -1);
+                               }
+                               throw new System.Net.WebException(e.Message, e);
                        } finally {
                                runReleaseResponse();
                                
@@ -140,7 +184,6 @@ namespace NaGet.SubCommands.SubTask
                                        NotifyCancelled();
                                } else {
                                        NotifyCompleted();
-                                       RaiseTaskSetEvent(TaskEventType.COMPLETED, "ダウンロード終了", 100);
                                }
                        }
                }
@@ -166,9 +209,8 @@ namespace NaGet.SubCommands.SubTask
                        
                        HttpWebRequest httpRequest = request as HttpWebRequest;
                        if (httpRequest != null) {
-                               httpRequest.AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip;  
-                               // TODO User-Agent
-                               httpRequest.UserAgent = "AppliStation/1.3";
+                               httpRequest.AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip;
+                               httpRequest.UserAgent = NaGet.Env.UserAgentString;
                        }
                }
                
@@ -198,21 +240,33 @@ namespace NaGet.SubCommands.SubTask
                        } catch (Exception) {
                        }
                        
-                       if (File.Exists(filepath)) { // ファイルが存在するとき削除
+                       // パス名を変えるときは、HTTPヘッダから取得したファイル名に変更する。
+                       if (enableChangeFileName && (!string.IsNullOrEmpty(downloadedFileName))) {
+                               filepath = Path.Combine(Path.GetDirectoryName(filepath), downloadedFileName);
+                       }
+                       
+                       // ファイルが存在するとき削除
+                       if (File.Exists(filepath)) {
                                File.Delete(filepath);
                        }
+                       // 部分ファイルが存在するときも削除 TODO レジューム処理
+                       if (File.Exists(filepath + PartialFilePostfix)) {
+                               File.Delete(filepath + PartialFilePostfix);
+                       }
                }
                
                private void runDownloadToFile()
                {
                        Stopwatch stopwatch = new Stopwatch();
+                       string partialFilepath = filepath + PartialFilePostfix;
                        
                        using (Stream stream = response.GetResponseStream() )
-                       using (FileStream fs = new FileStream(filepath,
+                       using (FileStream fs = new FileStream(partialFilepath,
                                                    FileMode.Create,
                                                    FileAccess.Write) ) {
+                               
                                try {
-                                       File.SetAttributes(filepath, FileAttributes.Hidden);
+                                       File.SetAttributes(partialFilepath, FileAttributes.Hidden);
                                        long contentLength = response.ContentLength;
        
                                        stopwatch.Start();
@@ -239,8 +293,6 @@ namespace NaGet.SubCommands.SubTask
                                        } finally {
                                                timer.Dispose();
                                        }
-                                       
-                                       File.SetAttributes(filepath, FileAttributes.Normal);
                                } catch (IOException ex) {
                                        if (cancelCalled) {
                                                throw new TaskCanceledException(string.Empty);
@@ -254,10 +306,15 @@ namespace NaGet.SubCommands.SubTask
                                        }
                                }
                        }
+                       
+                       if (File.Exists(partialFilepath)) {
+                               File.Move(partialFilepath, filepath);
+                               File.SetAttributes(filepath, FileAttributes.Normal);
+                       }
                }
                
                private void runPostprocess()
-               {                       
+               {
                        // 更新日を補完
                        if (File.Exists(filepath)) {
                                HttpWebResponse httpResponse = response as HttpWebResponse;
@@ -294,6 +351,16 @@ namespace NaGet.SubCommands.SubTask
                                string contentDisposition = httpresp.Headers["Content-Disposition"];
                                
                                if (! string.IsNullOrEmpty(contentDisposition)) {
+                                       // おかしな Content-Disposition ヘッダ向け
+                                       //(Content-Disposition: filename=\"...\" に attachment; を補って RFC1806 に準拠させる)
+                                       if (System.Text.RegularExpressions.Regex.IsMatch(contentDisposition, @" *filename=", System.Text.RegularExpressions.RegexOptions.IgnoreCase)) {
+                                               contentDisposition = "attachment; " + contentDisposition;
+                                       }
+                                       // "atachment;filename=\""のようにセミコロンの後ろにスペースがない場合、それを補充する
+                                       if (System.Text.RegularExpressions.Regex.IsMatch(contentDisposition, @";[^ ]", System.Text.RegularExpressions.RegexOptions.IgnoreCase)) {
+                                               contentDisposition = string.Join("; ", System.Text.RegularExpressions.Regex.Split(contentDisposition, @"; *"));
+                                       }
+                                       
                                        try {
                                                ContentDisposition parser = new ContentDisposition(contentDisposition);
                                                if (! string.IsNullOrEmpty(parser.FileName)) {
@@ -327,7 +394,7 @@ namespace NaGet.SubCommands.SubTask
                        // スループット・残り時間の算出
                        if (elapsedms > 0) {
                                byteps = 1000 * downloadsize / elapsedms;
-                               if (filesize > 0) {
+                               if (filesize > 0 && byteps > 0) {
                                        eta = TimeSpan.FromSeconds((filesize - downloadsize) / byteps);
                                }
                        }
@@ -335,7 +402,7 @@ namespace NaGet.SubCommands.SubTask
                        System.Text.StringBuilder msgbuilder = new System.Text.StringBuilder();
                        msgbuilder.AppendFormat("{0} bytes", downloadsize);
                        if (percent > 0) {
-                               msgbuilder.AppendFormat(" ({0}%)", percent);
+                               msgbuilder.AppendFormat(" ({0:F2}%)", percent);
                        }
                        if (eta.Ticks > 0) {
                                msgbuilder.AppendFormat(" ETA {0}", eta);