OSDN Git Service

1.52.1.658
[gikonavigoeson/gikonavi.git] / ItemDownload.pas
index e497de5..c229d17 100644 (file)
@@ -5,7 +5,8 @@ interface
 uses
        Windows, SysUtils, Classes, ComCtrls, Controls, Forms, IdHTTP,
        {HTTPApp,} YofUtils, IdGlobal, IdException, IdComponent, IniFiles, {DateUtils,}
-       GikoSystem, BoardGroup, MonaUtils, ExternalBoardManager;
+       GikoSystem, BoardGroup, MonaUtils, ExternalBoardManager, ExternalBoardPlugInMain,
+       Sort;
 
 type
        TDownloadItem = class;
@@ -15,7 +16,7 @@ type
        TGikoDLProgress = (gdpStd, gdpAll, gdpDatOchi, gdpOfflaw);
 
        TGikoWorkEvent = procedure(Sender: TObject; AWorkMode: TWorkMode; const AWorkCount: Integer; ID: Integer) of object;
-       TGikoWorkBeginEvent = procedure(Sender: TObject; AWorkMode: TWorkMode; const AWorkCountMax: Integer; ID: Integer) of object;
+       TGikoWorkBeginEvent = procedure(Sender: TObject; AWorkMode: TWorkMode; const AWorkCountMax: Integer; ID: Integer; const AWorkTitle: string) of object;
        TGikoWorkEndEvent = procedure(Sender: TObject; AWorkMode: TWorkMode; ID: Integer) of object;
        TDownloadEndEvent = procedure(Sender: TObject; Item: TDownloadItem) of object;
        TDownloadMsgEvent = procedure(Sender: TObject; Item: TDownloadItem; Msg: string; Icon: TGikoMessageIcon) of object;
@@ -41,6 +42,7 @@ type
                FOnWorkEnd: TGikoWorkEndEvent;
                FOnDownloadEnd: TDownloadEndEvent;
                FOnDownloadMsg: TDownloadMsgEvent;
+               FDownloadTitle: string;
 
                procedure FireDownloadEnd;
                procedure FireDownloadMsg;
@@ -49,16 +51,18 @@ type
                procedure WorkEnd(Sender: TObject; AWorkMode: TWorkMode);
                procedure Work(Sender: TObject; AWorkMode: TWorkMode; const AWorkCount: Integer);
                function ParseCgiStatus(Content: string): TCgiStatus;
-    function CgiDownload(ItemType: TGikoDownloadType; URL: string; Modified: TDateTime): Boolean;
+               function CgiDownload(ItemType: TGikoDownloadType; URL: string; Modified: TDateTime): Boolean;
                function DatDownload(ItemType: TGikoDownloadType; URL: string; Modified: TDateTime; RangeStart: Integer; AdjustLen: Integer): Boolean;
-               function DeleteStatusLine(Content: string): string;
+               procedure DeleteStatusLine(Item: TDownloadItem);
+               procedure InitHttpClient(client: TIdHttp);
+               procedure ClearHttpClient(client: TIdHttp);
        protected
                procedure Execute; override;
        public
                property Item: TDownloadItem read FItem write FItem;
                property Number: Integer read FNumber write FNumber;
                constructor Create(CreateSuspended: Boolean);
-               destructor Destroy; override;
+                               destructor Destroy; override;
                procedure Abort;
                property OnWork: TGikoWorkEvent read FOnWork write FOnWork;
                property OnWorkBegin: TGikoWorkBeginEvent read FOnWorkBegin write FOnWorkBegin;
@@ -79,6 +83,8 @@ type
                FResponseCode: Smallint;
                FState: TGikoDownloadState;
                FErrText: string;
+               FForceDownload: Boolean;
+               FIsAbone : Boolean;
        public
                procedure SaveListFile;
                procedure SaveItemFile;
@@ -93,10 +99,15 @@ type
                property ResponseCode: Smallint read FResponseCode write FResponseCode;
                property State: TGikoDownloadState read FState write FState;
                property ErrText: string read FErrText write FErrText;
+               property ForceDownload: Boolean read FForceDownload write FForceDownload;
+               property IsAbone : Boolean read FIsAbone write FIsAbone;
        end;
 
 implementation
 
+uses
+       Y_TextConverter, MojuUtils, HTMLCreate;
+
 constructor TDownloadThread.Create(CreateSuspended: Boolean);
 begin
        inherited Create(CreateSuspended);
@@ -109,6 +120,7 @@ end;
 
 destructor TDownloadThread.Destroy;
 begin
+       ClearHttpClient(FIndy);
        FIndy.Free;
        inherited;
 end;
@@ -116,21 +128,79 @@ end;
 function RFC1123_Date(aDate : TDateTime) : String;
 const
         StrWeekDay : String = 'MonTueWedThuFriSatSun';
-        StrMonth   : String = 'JanFebMarAprMayJunJulAugSepOctNovDec';
+        StrMonth        : String = 'JanFebMarAprMayJunJulAugSepOctNovDec';
 var
-        Year, Month, Day       : Word;
-        Hour, Min,   Sec, MSec : Word;
-        DayOfWeek              : Word;
+        Year, Month, Day                        : Word;
+        Hour, Min,      Sec, MSec : Word;
+        DayOfWeek                                                      : Word;
 begin
         DecodeDate(aDate, Year, Month, Day);
-        DecodeTime(aDate, Hour, Min,   Sec, MSec);
+        DecodeTime(aDate, Hour, Min,    Sec, MSec);
         DayOfWeek := ((Trunc(aDate) - 2) mod 7);
         Result := Copy(StrWeekDay, 1 + DayOfWeek * 3, 3) + ', ' +
                                                 Format('%2.2d %s %4.4d %2.2d:%2.2d:%2.2d',
                                                                                [Day, Copy(StrMonth, 1 + 3 * (Month - 1), 3),
                                                                                 Year, Hour, Min, Sec]);
 end;
-
+// ******************************************************************
+// HTTPClient\82Ì\8f\89\8aú\89»
+// ******************************************************************
+procedure TDownloadThread.InitHttpClient(client: TIdHttp);
+begin
+       ClearHttpClient(client);
+       client.Disconnect;
+       client.Request.UserAgent := GikoSys.GetUserAgent;
+       client.RecvBufferSize := Gikosys.Setting.RecvBufferSize;
+       client.ProxyParams.BasicAuthentication := False;
+       client.ReadTimeout := GikoSys.Setting.ReadTimeOut;
+       {$IFDEF DEBUG}
+       Writeln('------------------------------------------------------------');
+       {$ENDIF}
+       //FIndy.AllowCookies := False;
+       if GikoSys.Setting.ReadProxy then begin
+               if GikoSys.Setting.ProxyProtocol then
+                       client.ProtocolVersion := pv1_1
+               else
+                       client.ProtocolVersion := pv1_0;
+               client.ProxyParams.ProxyServer := GikoSys.Setting.ReadProxyAddress;
+               client.ProxyParams.ProxyPort := GikoSys.Setting.ReadProxyPort;
+               client.ProxyParams.ProxyUsername := GikoSys.Setting.ReadProxyUserID;
+               client.ProxyParams.ProxyPassword := GikoSys.Setting.ReadProxyPassword;
+               if GikoSys.Setting.ReadProxyUserID <> '' then
+                       client.ProxyParams.BasicAuthentication := True;
+               {$IFDEF DEBUG}
+               Writeln('\83v\83\8d\83L\83V\90Ý\92è\82 \82è');
+               Writeln('\83z\83X\83g: ' + GikoSys.Setting.ReadProxyAddress);
+               Writeln('\83|\81[\83g: ' + IntToStr(GikoSys.Setting.ReadProxyPort));
+               {$ENDIF}
+       end else begin
+               if GikoSys.Setting.Protocol then
+                       client.ProtocolVersion := pv1_1
+               else
+                       client.ProtocolVersion := pv1_0;
+               client.ProxyParams.ProxyServer := '';
+               client.ProxyParams.ProxyPort := 80;
+               client.ProxyParams.ProxyUsername := '';
+               client.ProxyParams.ProxyPassword := '';
+               {$IFDEF DEBUG}
+               Writeln('\83v\83\8d\83L\83V\90Ý\92è\82È\82µ');
+               {$ENDIF}
+       end;
+end;
+// ******************************************************************
+// HTTPClient\82Ì\83\8a\83N\83G\83X\83g\82Æ\83\8c\83X\83|\83\93\83X\82Ì\83f\81[\83^\82Ì\8fÁ\8b\8e
+// ******************************************************************
+procedure TDownloadThread.ClearHttpClient(client: TIdHttp);
+begin
+       client.Request.CustomHeaders.Clear;
+       client.Request.RawHeaders.Clear;
+       client.Request.Clear;
+       client.Response.CustomHeaders.Clear;
+       client.Response.RawHeaders.Clear;
+       client.Response.Clear;
+
+       client.ProxyParams.Clear;
+end;
 procedure TDownloadThread.Execute;
 var
        ResStream: TMemoryStream;
@@ -143,91 +213,84 @@ var
        Idx: Integer;
        ATitle: string;
        DownloadResult: Boolean;
-       Abone: Boolean;
-  foundPos: Integer;
-  boardPlugIn : TBoardPlugIn;
-  listContent : string;
+       boardPlugIn     : TBoardPlugIn;
+       lastContent             : string;
+       logFile                         : TFileStream;
+       adjustMargin    : Integer;
+const
+       ADJUST_MARGIN   = 16;
 begin
        while not Terminated do begin
                //===== \83v\83\89\83O\83C\83\93
-    boardPlugIn := nil;
-    ExternalBoardManager.OnWork                                := Work;
-    ExternalBoardManager.OnWorkBegin   := WorkBegin;
-    ExternalBoardManager.OnWorkEnd             := WorkEnd;
-
-    case FItem.FDownType of
-    gdtBoard:
-      begin
-        if FItem.FBoard <> nil then begin
-          if FItem.FBoard.IsBoardPlugInAvailable then begin
-            boardPlugIn        := FItem.FBoard.BoardPlugIn;
-            Item.State := TGikoDownloadState( boardPlugIn.DownloadBoard( DWORD( FItem.FBoard ) ) );
-          end;
-        end;
-      end;
-    gdtThread:
-      begin
-        if FItem.FThreadItem <> nil then begin
-          if FItem.FThreadItem.IsBoardPlugInAvailable then begin
-            boardPlugIn        := FItem.FThreadItem.BoardPlugIn;
-            Item.State := TGikoDownloadState( boardPlugIn.DownloadThread( DWORD( FItem.FThreadItem ) ) );
-          end;
-        end;
-      end;
-    end;
-
-    if boardPlugIn <> nil then begin
-      if FAbort then
-        Item.State := gdsAbort;
-      if Assigned( OnDownloadEnd ) then
-        Synchronize( FireDownloadEnd );
-      if Terminated then
-        Break;
-
-      Suspend;
-      Continue;
-    end;
+               FAbort := False;
+               boardPlugIn := nil;
+               ExternalBoardManager.OnWork                             := Work;
+               ExternalBoardManager.OnWorkBegin        := WorkBegin;
+               ExternalBoardManager.OnWorkEnd          := WorkEnd;
+
+               FDownloadTitle := '';
+               case FItem.FDownType of
+               gdtBoard:
+                       begin
+                               FDownloadTitle := FItem.FBoard.Title;
+                               if FItem.FBoard <> nil then begin
+                                       if FItem.FBoard.IsBoardPlugInAvailable then begin
+                                               boardPlugIn     := FItem.FBoard.BoardPlugIn;
+                                               Item.State      := TGikoDownloadState( boardPlugIn.DownloadBoard( DWORD( FItem.FBoard ) ) );
+                                       end;
+                               end;
+                       end;
+               gdtThread:
+                       begin
+                               FDownloadTitle := FItem.FThreadItem.Title;
+                               if FItem.FThreadItem <> nil then begin
+                                       if FItem.FThreadItem.ParentBoard.IsBoardPlugInAvailable then begin
+                                               boardPlugIn := FItem.FThreadItem.ParentBoard.BoardPlugIn;
+                                               Item.State      := TGikoDownloadState( boardPlugIn.DownloadThread( DWORD( FItem.FThreadItem ) ) );
+                                       end;
+                                       //if FItem.FThreadItem.IsBoardPlugInAvailable then begin
+                                       //      boardPlugIn     := FItem.FThreadItem.BoardPlugIn;
+                                       //      Item.State      := TGikoDownloadState( boardPlugIn.DownloadThread( DWORD( FItem.FThreadItem ) ) );
+                                       //end;
+                               end;
+                       end;
+               end;
+               if Length(FDownloadTitle) = 0 then
+                       FDownloadTitle := '\81i\96¼\8fÌ\95s\96¾\81j';
+
+               if boardPlugIn <> nil then begin
+                       if FAbort then begin
+                               Item.State := gdsAbort;
+                       end;
+                       if Assigned( OnDownloadEnd ) then
+                               Synchronize( FireDownloadEnd );
+                       if Terminated then
+                               Break;
+
+                       Suspend;
+                       Continue;
+               end;
 
-               //===== \83v\83\89\83O\83C\83\93\82ð\8eg\97p\82µ\82È\82¢\8fê\8d\87
                FAbort := False;
-               FIndy.Request.CustomHeaders.Clear;
-               FIndy.Response.Clear;
-               FIndy.Request.Clear;
-               FIndy.Request.UserAgent := GikoSys.GetUserAgent;
-               FIndy.RecvBufferSize := Gikosys.Setting.RecvBufferSize;
-               FIndy.ProxyParams.BasicAuthentication := False;
-               {$IFDEF DEBUG}
-               Writeln('------------------------------------------------------------');
-               {$ENDIF}
-               //FIndy.AllowCookies := False;
-               if GikoSys.Setting.ReadProxy then begin
-                       if GikoSys.Setting.ProxyProtocol then
-                               FIndy.ProtocolVersion := pv1_1
-                       else
-                               FIndy.ProtocolVersion := pv1_0;
-                       FIndy.ProxyParams.ProxyServer := GikoSys.Setting.ReadProxyAddress;
-                       FIndy.ProxyParams.ProxyPort := GikoSys.Setting.ReadProxyPort;
-                       FIndy.ProxyParams.ProxyUsername := GikoSys.Setting.ReadProxyUserID;
-                       FIndy.ProxyParams.ProxyPassword := GikoSys.Setting.ReadProxyPassword;
-                       if GikoSys.Setting.ReadProxyUserID <> '' then
-                               FIndy.ProxyParams.BasicAuthentication := True;
-                       {$IFDEF DEBUG}
-                       Writeln('\83v\83\8d\83L\83V\90Ý\92è\82 \82è');
-                       Writeln('\83z\83X\83g: ' + GikoSys.Setting.ReadProxyAddress);
-                       Writeln('\83|\81[\83g: ' + IntToStr(GikoSys.Setting.ReadProxyPort));
-                       {$ENDIF}
-               end else begin
-                       if GikoSys.Setting.Protocol then
-                               FIndy.ProtocolVersion := pv1_1
-                       else
-                               FIndy.ProtocolVersion := pv1_0;
-                       FIndy.ProxyParams.ProxyServer := '';
-                       FIndy.ProxyParams.ProxyPort := 80;
-                       FIndy.ProxyParams.ProxyUsername := '';
-                       FIndy.ProxyParams.ProxyPassword := '';
-                       {$IFDEF DEBUG}
-                       Writeln('\83v\83\8d\83L\83V\90Ý\92è\82È\82µ');
-                       {$ENDIF}
+               //===== \83v\83\89\83O\83C\83\93\82ð\8eg\97p\82µ\82È\82¢\8fê\8d\87
+               InitHttpClient(FIndy);
+               adjustMargin := 0;
+               if Item.DownType = gdtThread then begin
+                       if FileExists( Item.ThreadItem.GetThreadFileName ) then begin
+                               // dat \83t\83@\83C\83\8b\82Ì\8dÅ\8cã\82ð\93Ç\82Ý\8fo\82·
+                               SetLength( lastContent, ADJUST_MARGIN + 1 );
+                               logFile := TFileStream.Create( Item.ThreadItem.GetThreadFileName, fmOpenRead or fmShareDenyWrite );
+                               try
+                                       logFile.Seek( -(ADJUST_MARGIN + 1), soFromEnd );
+                                       logFile.Read( lastContent[ 1 ], ADJUST_MARGIN + 1 );
+                                       lastContent := StringReplace( lastContent, #13, '', [] );       // CR \82Ì\8dí\8f\9c
+                               finally
+                                       logFile.Free;
+                               end;
+                       end else begin
+        lastContent := '';
+                       end;
+                       adjustMargin := Length( lastContent );
                end;
 
                FIndy.Request.ContentRangeStart := 0;
@@ -249,7 +312,19 @@ begin
                                        Writeln('Modified: ' + FloatToStr(Item.Board.LastModified));
                                        {$ENDIF}
                                        URL := Item.Board.GetReadCgiURL;
-                                       Modified := Item.Board.LastModified;
+                                       if Item.ForceDownload then begin
+                                               // \8b­\90§\8eæ\93¾
+                                               ATitle := Item.Board.Title;
+                                               if ATitle = '' then
+                                                       ATitle := '\81i\96¼\8fÌ\95s\96¾\81j';
+                                               FMsg := '\81\9a\8b­\90§\8eæ\93¾\82ð\8ds\82¢\82Ü\82· - [' + ATitle + ']';
+                                               FIcon := gmiWhat;
+                                               if Assigned(OnDownloadMsg) then
+                                                       Synchronize(FireDownloadMsg);
+                                               Modified := ZERO_DATE
+                                       end else begin
+                                               Modified := Item.Board.LastModified;
+                                       end;
                                end else if Item.DownType = gdtThread then begin
                                        {$IFDEF DEBUG}
                                        Writeln('DAT\8eæ\93¾');
@@ -257,27 +332,45 @@ begin
                                        Writeln('Modified: ' + FloatToStr(Item.ThreadItem.LastModified));
                                        {$ENDIF}
                                        URL := Item.ThreadItem.GetDatURL;
-                                       Modified := Item.ThreadItem.LastModified;
-                                       if Item.ThreadItem.Size > 0 then begin
-                                               {$IFDEF DEBUG}
-                                               Writeln('RangeStart: ' + IntToStr(Item.ThreadItem.Size));
-                                               {$ENDIF}
-                                               //\82 \82Ú\81[\82ñ\83`\83F\83b\83N\82Ì\82½\82ß\82P\83o\83C\83g\91O\82©\82ç\8eæ\93¾
-                                               RangeStart := Item.ThreadItem.Size;
-                                               AdjustLen := -1;
+                                       if Item.ForceDownload then begin
+                                               // \8b­\90§\8eæ\93¾
+                                               ATitle := Item.ThreadItem.Title;
+                                               if ATitle = '' then
+                                                       ATitle := '\81i\96¼\8fÌ\95s\96¾\81j';
+                                               FMsg := '\81\9a\8b­\90§\8eæ\93¾\82ð\8ds\82¢\82Ü\82· - [' + ATitle + ']';
+                                               FIcon := gmiWhat;
+                                               if FileExists(ChangeFileExt(Item.FThreadItem.GetThreadFileName,'.NG')) = true then
+                                                       DeleteFile(ChangeFileExt(Item.FThreadItem.GetThreadFileName,'.NG'));
+                                               if Assigned(OnDownloadMsg) then
+                                                       Synchronize(FireDownloadMsg);
+                                               Modified := ZERO_DATE;
+                                               RangeStart := 0;
+                                               AdjustLen := 0;
+                                       end else begin
+                                               Modified := Item.ThreadItem.LastModified;
+                                               if Item.ThreadItem.Size > 0 then begin
+                                                       {$IFDEF DEBUG}
+                                                       Writeln('RangeStart: ' + IntToStr(Item.ThreadItem.Size));
+                                                       {$ENDIF}
+                                                       // \82 \82Ú\81[\82ñ\83`\83F\83b\83N\82Ì\82½\82ß adjustMargin \83o\83C\83g\91O\82©\82ç\8eæ\93¾
+                                                       RangeStart := Item.ThreadItem.Size;
+                                                       AdjustLen := -adjustMargin;
+                                               end;
                                        end;
                                end;
-                               Abone := False;
+                               Item.IsAbone := False;
                                DownloadResult := DatDownload(Item.DownType, URL, Modified, RangeStart, AdjustLen);
                                {$IFDEF DEBUG}
                                Writeln('ResponseCode: ' + IntToStr(FIndy.ResponseCode));
                                {$ENDIF}
                                if Item.DownType = gdtThread then begin
                                        if Item.ResponseCode = 416 then begin
-                                               Abone := True;
+                                               Item.IsAbone := True;
                                                DownloadResult := True;
-                                       end else if DownloadResult and (AdjustLen = -1) and (Item.Content[1] <> #10) then
-                                               Abone := True;
+                                       end else if DownloadResult and (AdjustLen < 0) then begin
+                                               if Copy( Item.Content, 1, adjustMargin ) <> lastContent then
+                                                       Item.IsAbone := True;
+                                       end;
                                end;
 
                                if Trim(FIndy.Response.RawHeaders.Values['Date']) <> '' then begin
@@ -291,7 +384,7 @@ begin
                                        {$IFDEF DEBUG}
                                        Writeln('Date:' + FIndy.Response.RawHeaders.Values['Date']);
                                        {$ENDIF}
-                                       if Abone then begin
+                                       if Item.IsAbone then begin
                                                {$IFDEF DEBUG}
                                                Writeln('\82 \82Ú\81[\82ñ\8c\9f\8fo');
                                                {$ENDIF}
@@ -303,8 +396,8 @@ begin
                                                AdjustLen := 0;
                                                FMsg := '\81\9a\81u\82 \82Ú\81[\82ñ\81v\82ð\8c\9f\8fo\82µ\82½\82Ì\82Å\8dÄ\8eæ\93¾\82ð\8ds\82¢\82Ü\82· - [' + ATitle + ']';
                                                FIcon := gmiWhat;
-                        if FileExists(ChangeFileExt(Item.FThreadItem.GetThreadFileName,'.NG')) = true then
-                                               DeleteFile(ChangeFileExt(Item.FThreadItem.GetThreadFileName,'.NG'));
+                                               if FileExists(ChangeFileExt(Item.FThreadItem.GetThreadFileName,'.NG')) = true then
+                                                       DeleteFile(ChangeFileExt(Item.FThreadItem.GetThreadFileName,'.NG'));
                                                if Assigned(OnDownloadMsg) then
                                                        Synchronize(FireDownloadMsg);
                                                if not DatDownload(Item.DownType, URL, ZERO_DATE, RangeStart, AdjustLen) then
@@ -313,9 +406,9 @@ begin
                                                Writeln('\82 \82Ú\81[\82ñ\8dÄ\8eæ\93¾\8cã');
                                                Writeln('ResponseCode: ' + IntToStr(Item.ResponseCode));
                                                {$ENDIF}
-                                       end else if (Item.DownType = gdtThread) and (AdjustLen = -1) and (Item.Content[1] = #10) then begin
-                                               //\8d·\95ª\8eæ\93¾\82©\82Â\82P\83o\83C\83g\96Ú\82ªLF\82Ì\8fê\8d\87\81i\90³\8fí\8eæ\93¾\81j\82Í\93ª\82ÌLF\82ð\8dí\8f\9c
-                                               Item.Content := Copy(Item.Content, 2, Length(Item.Content));
+                                       end else if (Item.DownType = gdtThread) and (AdjustLen 0) then begin
+                                               // \8d·\95ª\8eæ\93¾\82ª\8fo\97\88\82½\8fê\8d\87\82Í\82 \82Ú\81[\82ñ\83`\83F\83b\83N\97p\82É\8eæ\93¾\82µ\82½\97]\95ª\82È\83T\83C\83Y\82ð\8dí\8f\9c
+                                               Item.Content := Copy(Item.Content, adjustMargin + 1, MaxInt);
                                        end;
                                end else begin
                                        Item.State := gdsError;
@@ -353,6 +446,28 @@ begin
                                end;
 
                                //********************
+                               //dat.gz\81@\81¨\81@dat\82Ì\8eæ\93¾\81@2005\94N6\8c\8e\92Ç\89Á\81@by\82à\82\82ã
+                               //********************
+                               if (Item.DownType = gdtThread) and (Item.ResponseCode = 302) then begin
+                                       {$IFDEF DEBUG}
+                                       Writeln('dat\8eæ\93¾');
+                                       {$ENDIF}
+                                       FMsg := '\89ß\8b\8e\83\8d\83O(dat.gz)\82ª\91\8dÝ\82µ\82È\82¢\82½\82ß\89ß\8b\8e\83\8d\83O(dat)\82ð\92T\82µ\82Ü\82· - [' + ATitle + ']';
+                                       FIcon := gmiWhat;
+                                       if Assigned(OnDownloadMsg) then
+                                               Synchronize(FireDownloadMsg);
+                                       URL := ChangeFileExt(URL, '');
+                                       Modified := Item.ThreadItem.LastModified;
+                                       RangeStart := 0;
+                                       AdjustLen := 0;
+                                       if not DatDownload(Item.DownType, URL, Modified, RangeStart, AdjustLen) then
+                                               Item.State := gdsError;
+                                       {$IFDEF DEBUG}
+                                       Writeln('ResponseCode: ' + IntToStr(Item.ResponseCode));
+                                       {$ENDIF}
+                               end;
+
+                               //********************
                                //dat.gz\8eæ\93¾(2)
                                //********************
 {
@@ -417,8 +532,7 @@ begin
                                                                Writeln('CGIStatus: OK');
                                                                {$ENDIF}
                                                                Item.ResponseCode := 200;
-                                                               Item.Content := DeleteStatusLine(Item.Content);
-                                                               Item.ContentLength := CgiStatus.FSize;
+                                                               DeleteStatusLine(Item);
                                                        end;
                                                        gcsINCR: begin
                                                                //\8d¡\82Í\82 \82è\82¦\82È\82¢
@@ -426,8 +540,7 @@ begin
                                                                Writeln('CGIStatus: 206');
                                                                {$ENDIF}
                                                                Item.ResponseCode := 206;
-                                                               Item.Content := DeleteStatusLine(Item.Content);
-                                                               Item.ContentLength := CgiStatus.FSize;
+                                                               DeleteStatusLine(Item);
                                                        end;
                                                        gcsERR: begin
                                                                {$IFDEF DEBUG}
@@ -452,9 +565,13 @@ begin
                                                        Idx := Pos(' ', Item.ErrText);
                                                        if Idx <> 0 then begin
                                                                URL := Copy(Item.ErrText, Idx + 1, Length(Item.ErrText));
-                                                               if Pos('../', URL) = 1 then
-                                                                       URL := Copy(URL, 4, Length(URL));
-                                                               URL := GikoSys.UrlToServer(Item.ThreadItem.ParentBoard.URL) + URL;
+                                                               if Pos( '://', URL ) = 0 then begin
+                                                                       if Pos('../', URL) = 1 then
+                                                                               URL := Copy(URL, 4, MaxInt );
+                                                                       if Pos( '/', URL ) = 1 then
+                                                                               URL := Copy( URL, 2, MaxInt );
+                                                                       URL := GikoSys.UrlToServer(Item.ThreadItem.ParentBoard.URL) + URL;
+                                                               end;
                                                                Modified := Item.ThreadItem.LastModified;
                                                                RangeStart := 0;
                                                                AdjustLen := 0;
@@ -481,40 +598,6 @@ begin
                                        end;
                                end;
 
-                               {$IFDEF DEBUG}
-                               if (Item.DownType = gdtThread) and (Item.ResponseCode = 302) then begin
-                                       ATitle := Item.ThreadItem.Title;
-                                       if ATitle = '' then
-                                               ATitle := '\81i\96¼\8fÌ\95s\96¾\81j';
-                                       FMsg := '\81\9a\89ß\8b\8e\83\8d\83O(1)\82ª\91\8dÝ\82µ\82È\82¢\82½\82ßgoogle\83L\83\83\83b\83V\83\85\82©\82ç\92T\82µ\82Ü\82· - [' + ATitle + ']';
-                                       FIcon := gmiWhat;
-                                       if Assigned(OnDownloadMsg) then
-                                               Synchronize(FireDownloadMsg);
-          URL := 'http://www.google.co.jp/search?q=cache:' + Item.ThreadItem.URL;
-                                       URL := StringReplace( URL, 'l50', '', [rfReplaceAll] );
-                                       Modified := Item.ThreadItem.LastModified;
-          Repeat
-            if not CgiDownload(Item.DownType, URL, Modified) then
-              Item.State := gdsError;
-            URL := FIndy.Response.Location;
-          Until Item.ResponseCode <> 301;
-          if Item.ResponseCode = 200 then begin
-               foundPos := Pos( '<dt>', Item.Content ) + Length( '<dt>' );
-               Item.Content := Copy( Item.Content, foundPos, Length( Item.Content ) );
-            foundPos := Pos( '</dl>', Item.Content );
-            If foundPos > 0 Then
-                       Item.Content := Copy( Item.Content, 1, foundPos - 1 );
-            Item.Content := StringReplace( Item.Content, '<dt>', '<>' + #13#10, [rfReplaceAll] );
-            Item.Content := StringReplace( Item.Content, '<a href="mailto:', '', [rfReplaceAll] );
-            Item.Content := StringReplace( Item.Content, '"><b>', '<>', [rfReplaceAll] );
-            Item.Content := StringReplace( Item.Content, '<b>', '<>', [rfReplaceAll] );
-            Item.Content := StringReplace( Item.Content, '</b></a>', '<>', [rfReplaceAll] );
-            Item.Content := StringReplace( Item.Content, '</b>', '<>', [rfReplaceAll] );
-            Item.Content := StringReplace( Item.Content, '<dd>', '<>', [rfReplaceAll] );
-          end;
-                               end;
-                               {$ENDIF}
-
                                case Item.ResponseCode of
                                        200: Item.State := gdsComplete;
                                        206: Item.State := gdsDiffComplete;
@@ -522,68 +605,9 @@ begin
                                        else
                                                Item.State := gdsError;
                                end;
-{
-                               //\96³\82¢\82Æ\8ev\82¤\82¯\82Ç\81B\81B\81B
-                               if (Item.ResponseCode in [200, 206]) and (Item.Content = '') then
-                                       Item.State := gdsError;
-                               Item.LastModified := FIndy.Response.LastModified;
-                               //\8d·\95ª\8eæ\93¾\82Å\82P\83o\83C\83g\91O\82©\82ç\82Æ\82Á\82Ä\82«\82½\82Æ\82«\82Í\83}\83C\83i\83X\82·\82é
-                               Item.ContentLength := FIndy.Response.ContentLength + AdjustLen;
-                               try
-                                       ResStream.Clear;
-                                       FIndy.Get(URL, ResStream);
-                                       Item.Content := GikoSys.GzipDecompress(ResStream, FIndy.Response.ContentEncoding);
-                                       if (Item.DownType = gdtThread) and (AdjustLen = -1) and (Item.Content[1] <> #10) then begin
-                                               //\8d·\95ª\8eæ\93¾\82©\82Â\82P\83o\83C\83g\96Ú\82ªLF\82Å\82È\82¢\8fê\8d\87\82Í\81u\82 \82Ú\81[\82ñ\81v\82³\82ê\82Ä\82¢\82é\82©\82à\82µ\82ê\82ñ\82Ì\82Å\8dÄ\8eæ\93¾
-                                               //\82±\82±\82Å\83\81\83b\83Z\81[\83W\95\\8e¦\83C\83x\83\93\83g
-                                               //event
-                                               FMsg := '\81u\82 \82Ú\81[\82ñ\81v\82ð\8c\9f\8fo\82µ\82½\82Ì\82Å\8dÄ\8eæ\93¾\82ð\8ds\82¢\82Ü\82·';
-                                               if Assigned(OnDownloadMsg) then
-                                                       Synchronize(FireDownloadMsg);
-                                               FIndy.Request.ContentRangeStart := 0;
-                                               FIndy.Request.ContentRangeEnd := 0;
-                                               AdjustLen := 0;
-                                               ResStream.Clear;
-                                               FIndy.Get(URL, ResStream);
-                                               Item.Content := GikoSys.GzipDecompress(ResStream, FIndy.Response.ContentEncoding);
-                                       end else if (Item.DownType = gdtThread) and (AdjustLen = -1) and (Item.Content[1] = #10) then begin
-                                               //\8d·\95ª\8eæ\93¾\82©\82Â\82P\83o\83C\83g\96Ú\82ªLF\82Ì\8fê\8d\87\81i\90³\8fí\8eæ\93¾\81j\82Í\93ª\82ÌLF\82ð\8dí\8f\9c
-                                               Item.Content := Copy(Item.Content, 2, Length(Item.Content));
-                                       end;
-                               except
-                                       Item.State := gdsError;
-                               end;
-                               Item.ResponseCode := FIndy.ResponseCode;
-}
-{
-                               try
-                                       ResStream.Clear;
-                                       FIndy.Get(URL, ResStream);
-                                       Item.Content := GikoSys.GzipDecompress(ResStream, FIndy.Response.ContentEncoding);
-                               except
-                                       Item.State := gdsError;
-                               end;
-
-                               CgiStatus := ParseCgiStatus(Item.Content);
-                               if CgiStatus.FStatus = gcsOK then begin
-                                       if CgiStatus.FSize = 0 then
-                                               Item.State := gdsNotModify
-                                       else if Item.DownType = gdtBoard then
-                                               Item.State := gdsComplete
-                                       else
-                                               Item.State := gdsDiffComplete;
-                               end else if CgiStatus.FStatus = gcsINCR then begin
-                                       Item.State := gdsComplete;
-                               end else if CgiStatus.FStatus = gcsERR then begin
-                                       Item.State := gdsError;
-                                       Item.ErrText := CgiStatus.FErrText;
-                               end;
-                               Item.ContentLength := CgiStatus.FSize;
-                               }
                        except
                                Item.State := gdsError;
                        end;
-                       //Item.ResponseCode := FIndy.ResponseCode;
                        if FAbort then
                                Item.State := gdsAbort;
                finally
@@ -591,6 +615,9 @@ begin
                                Synchronize(FireDownloadEnd);
                        ResStream.Free;
                end;
+
+               ClearHttpClient(FIndy);
+
                if Terminated then Break;
                Suspend;
        end;
@@ -610,7 +637,7 @@ begin
                FIndy.Request.LastModified := modified - OffsetFromUTC;
        end;
        FIndy.Request.AcceptEncoding := '';
-  FIndy.Request.Accept := 'text/html';
+       FIndy.Request.Accept := 'text/html';
        ResStream := TMemoryStream.Create;
        try
                try
@@ -640,7 +667,7 @@ begin
                                Item.ErrText := E.Message;
                                Result := False;
                                ResponseCode := -1;
-                Screen.Cursor := crDefault;
+                               Screen.Cursor := crDefault;
                        end;
                        on E: EIdConnectException do begin
                                Item.Content := '';
@@ -649,7 +676,7 @@ begin
                                Item.ErrText := E.Message;
                                Result := False;
                                ResponseCode := -1;
-                Screen.Cursor := crDefault;
+                               Screen.Cursor := crDefault;
                        end;
                        on E: Exception do begin
                                {$IFDEF DEBUG}
@@ -662,7 +689,7 @@ begin
                                Item.ErrText := E.Message;
                                ResponseCode := FIndy.ResponseCode;
                                Result := False;
-        Screen.Cursor := crDefault;
+                               Screen.Cursor := crDefault;
                        end;
                end;
        finally
@@ -681,8 +708,6 @@ var
 begin
        ResponseCode := -1;
        if (ItemType = gdtThread) and (RangeStart > 0) then begin
-//     if (ItemType = gdtThread) and (Item.ThreadItem.Size > 0) then begin
-//             FIndy.Request.ContentRangeStart := Item.ThreadItem.Size + AdjustLen;
                FIndy.Request.ContentRangeStart := RangeStart + AdjustLen;
                FIndy.Request.ContentRangeEnd := 0;
        end else begin
@@ -695,9 +720,7 @@ begin
        FIndy.Request.CustomHeaders.Add('Pragma: no-cache');
        if (Modified <> 0) and (Modified <> ZERO_DATE) then begin
                FIndy.Request.LastModified := modified - OffsetFromUTC;
-               //FIndy.Request.CustomHeaders.Add('If-Modified-Since: ' + RFC1123_Date(modified - OffsetFromUTC) + ' GMT');
        end;
-//     FIndy.Request.AcceptEncoding := 'gzip';
        if RangeStart = 0 then
                FIndy.Request.AcceptEncoding := 'gzip'
        else
@@ -710,7 +733,7 @@ begin
                        Writeln('URL: ' + URL);
                        {$ENDIF}
                        FIndy.Get(URL, ResStream);
-                       Item.Content := GikoSys.GzipDecompress(ResStream, FIndy.Response.ContentEncoding);
+            Item.Content := GikoSys.GzipDecompress(ResStream, FIndy.Response.ContentEncoding);
                        Item.LastModified := FIndy.Response.LastModified;
                        //\8d·\95ª\8eæ\93¾\82Å\82P\83o\83C\83g\91O\82©\82ç\82Æ\82Á\82Ä\82«\82½\82Æ\82«\82Í\83}\83C\83i\83X\82·\82é
 //                     Item.ContentLength := FIndy.Response.ContentLength + AdjustLen;
@@ -734,7 +757,7 @@ begin
                                Item.ErrText := E.Message;
                                Result := False;
                                ResponseCode := -1;
-                Screen.Cursor := crDefault;
+                               Screen.Cursor := crDefault;
                        end;
                        on E: EIdConnectException do begin
                                Item.Content := '';
@@ -743,7 +766,7 @@ begin
                                Item.ErrText := E.Message;
                                Result := False;
                                ResponseCode := -1;
-                Screen.Cursor := crDefault;
+                               Screen.Cursor := crDefault;
                        end;
                        on E: Exception do begin
                                {$IFDEF DEBUG}
@@ -756,7 +779,7 @@ begin
                                Item.ErrText := E.Message;
                                ResponseCode := FIndy.ResponseCode;
                                Result := False;
-                Screen.Cursor := crDefault;
+                               Screen.Cursor := crDefault;
                        end;
                end;
        finally
@@ -787,12 +810,15 @@ procedure TDownloadThread.Abort;
 begin
        FAbort := True;
        FIndy.DisconnectSocket;
+       if socket <> nil then begin
+               socket.DisconnectSocket;
+       end;
 end;
 
 procedure TDownloadThread.WorkBegin(Sender: TObject; AWorkMode: TWorkMode; const AWorkCountMax: Integer);
 begin
        if Assigned(OnWorkBegin) then
-               OnWorkBegin(Sender, AWorkMode, AWorkCountMax, FNumber);
+               OnWorkBegin(Sender, AWorkMode, AWorkCountMax, FNumber, FDownloadTitle);
 end;
 
 procedure TDownloadThread.WorkEnd(Sender: TObject; AWorkMode: TWorkMode);
@@ -820,8 +846,8 @@ begin
 // [-INCR] (Incorrect)\82Ì\8fê\8d\87\82Í\82·\82×\82Ä\82Ì\83f\81[\83^
 // [-ERR (\83e\83L\83X\83g)]\82Ì\8fê\8d\87\82Í\82È\82ñ\82©\83G\83\89\81[
 // \97á\81F+OK 23094/512K
-//     -INCR 23094/512K
-//     -ERR \82»\82ñ\82È\94Â\82È\82¢\82Å\82·
+//              -INCR 23094/512K
+//              -ERR \82»\82ñ\82È\94Â\82È\82¢\82Å\82·
        Idx := AnsiPos(#10, Content);
        StatusLine := Copy(Content, 0, Idx);
 
@@ -863,17 +889,20 @@ begin
        end;
 end;
 
-//\8eè\94²\82«\82È\8f\88\97\9d\82Å1\8ds\96Ú\82ð\8fÁ\82·
-function TDownloadThread.DeleteStatusLine(Content: string): string;
+//\82P\8ds\96Ú\82ð\8fÁ\82µ\82Ä\81A\83R\83\93\83e\83\93\83c\83T\83C\83Y\82ð\90Ý\92è\82·\82é
+procedure TDownloadThread.DeleteStatusLine(Item: TDownloadItem);
 var
        SList: TStringList;
 begin
        SList := TStringList.Create;
        try
-               SList.Text := Content;
+               SList.Text := Item.Content;
+               //1\8ds\96Ú\82ð\8dí\8f\9c
                if SList.Count > 1 then
                        SList.Delete(0);
-               Result := SList.Text;
+        Item.Content := SList.Text;
+               //\89ü\8ds\83R\81[\83h\82ðCRLF -> LF\82Æ\8dl\82¦\82Ä\81A\8ds\90\94\95ª\82¾\82¯\83}\83C\83i\83X
+        Item.ContentLength := Length(SList.Text) - SList.Count;
        finally
                SList.Free;
        end;
@@ -884,58 +913,64 @@ var
        i: Integer;
        index: Integer;
        NewItem: TThreadItem;
-//     SaveCount: Integer;
        NumCount: Integer;
        Body: TStringList;
        Rec: TSubjectRec;
+       {$IFDEF DEBUG}
+       st, rt : Cardinal;
+       {$ENDIF}
        function MakeThreadCallBack(
-       inInstance      : DWORD;        // TBoardItem \82Ì\83C\83\93\83X\83^\83\93\83X
+               inInstance      : DWORD;        // TBoardItem \82Ì\83C\83\93\83X\83^\83\93\83X
                inURL                           : PChar;        // \83X\83\8c\83b\83h\82Ì URL
                inTitle                 : PChar;        // \83X\83\8c\83^\83C
                inCount                 : DWORD         // \83\8c\83X\82Ì\90\94
        ) : Boolean; stdcall;           // \97ñ\8b\93\82ð\91±\82¯\82é\82È\82ç True
-  var
-       threadItem      : TThreadItem;
-    boardItem          : TBoard;
-  begin
-       Result          := True;
-    boardItem  := TBoard( inInstance );
-
-    boardItem.IntData := boardItem.IntData + 1;
-    // \81¦\8dì\82è\93r\92\86\81B\96{\93\96\82Í URL \82Å\82Í\82È\82­ FileName \82ð\88ø\90\94\82É\8eæ\82é\81¦
-    index := boardItem.GetIndex( string( inURL ) );
-    if index = -1 then begin
-      //\90V\82µ\82¢\83X\83\8c\83b\83h
-      threadItem := TThreadItem.Create( boardItem.BoardPlugIn, string( inURL ) );
-
-      threadItem.Title                         := string( inTitle );
-//                             threadItem.Count := inCount;
-      threadItem.ParentBoard   := Board;
-      threadItem.No                                            := boardItem.IntData;
-      threadItem.RoundDate             := ZERO_DATE;
-      threadItem.LastModified  := ZERO_DATE;
-      threadItem.AgeSage                       := gasNew;
-      boardItem.ListData.Add( threadItem );
-    end else begin
-      //boardItem.Items[index].Count := Count;
-      //boardItem.Items[index].Count := Rec.FCount;
-      if boardItem.Items[index].No > boardItem.IntData then
-        boardItem.Items[index].AgeSage := gasAge
-      else if boardItem.Items[index].AllResCount < inCount then
-        boardItem.Items[index].AgeSage := gasSage
-      else
-        boardItem.Items[index].AgeSage := gasNone;
-
-      boardItem.Items[index].No                                                := boardItem.IntData;
-      boardItem.Items[index].AllResCount       := inCount;
-//                             if not boardItem.Items[index].IsLogFile then
-//                                     boardItem.Items[index].Count := inCountt;
-      boardItem.ListData.Add( boardItem.Items[index] );
-      boardItem.DeleteList( index );
-    end;
-  end;
+       var
+               _ThreadItem     : TThreadItem;  // '_' \82Í\83N\83\89\83X\82Ì\83v\83\8d\83p\83e\83B\82Æ\82©\82Ô\82Á\82Ä\82é\82Ì\82Å
+               boardItem               : TBoard;
+       begin
+               Result          := True;
+               boardItem       := TBoard( inInstance );
+
+               boardItem.IntData := boardItem.IntData + 1;
+               if boardItem.IntData < (boardItem.Count shr      2) then
+                       index := boardItem.GetIndexFromURL( string( inURL ) )
+               else
+                       index := boardItem.GetIndexFromURL( string( inURL ), True );
+               if index = -1 then begin
+                       //\90V\82µ\82¢\83X\83\8c\83b\83h
+                       _ThreadItem := TThreadItem.Create( boardItem.BoardPlugIn, boardItem, string( inURL ) );
+
+                       _ThreadItem.Title                                       := string( inTitle );
+                       _ThreadItem.AllResCount         := inCount;
+                       _ThreadItem.ParentBoard         := Board;
+                       _ThreadItem.No                                          := boardItem.IntData;
+                       _ThreadItem.RoundDate           := ZERO_DATE;
+                       _ThreadItem.LastModified        := ZERO_DATE;
+                       _ThreadItem.AgeSage                             := gasNew;
+                       boardItem.Add(_ThreadItem);
+               end else begin
+                       //\8f\87\88Ê\82ª\8fã\82ª\82Á\82Ä\82¢\82ê\82ÎAge\82É\82·\82é
+                       if boardItem.Items[index].No > boardItem.IntData then
+                               boardItem.Items[index].AgeSage := gasAge
+                       //\8f\87\88Ê\82ª\8fã\82ª\82Á\82Ä\82È\82¢\82¯\82Ç\81A\83\8c\83X\82ª\82Â\82¢\82Ä\82½\82ç\81ASage\82É
+                       else if boardItem.Items[index].AllResCount <> inCount then
+                               boardItem.Items[index].AgeSage := gasSage
+                       //\8f\87\88Ê\8fã\82ª\82Á\82Ä\82È\82¢\82µ\81A\83\8c\83X\82Ì\91\9d\8c¸\82à\96³\82¯\82ê\82Î\81ANone
+                       else
+                               boardItem.Items[index].AgeSage := gasNone;
+
+                       boardItem.Items[index].No                                               := boardItem.IntData;
+                       boardItem.Items[index].AllResCount      := inCount;
+               end;
+
+       end;
 begin
-       Board.ListData := TList.Create;
+{$IFDEF DEBUG}
+       st := GetTickCount;
+       Writeln('SAVELIST');
+{$ENDIF}
+       //Board.ListData := TList.Create;
        Body := TStringList.Create;
        try
                //\83_\83E\83\93\83\8d\81[\83h\93ú\8e\9e\90Ý\92è\81i\83\8d\81[\83J\83\8b\93ú\8e\9e\81j
@@ -943,109 +978,124 @@ begin
                //\83T\81[\83o\8fã\83t\83@\83C\83\8b\82Ì\8dX\90V\8e\9e\8d\8f\90Ý\92è
                Board.LastModified := LastModified;
 
-    if Board.IsBoardPlugInAvailable then begin
-      // \90V\82µ\82¢\83\8a\83X\83g\82ð\8dì\90¬\82·\82é
-      // \90V\82µ\82¢\83\8a\83X\83g\82É\8cÃ\82¢\83\8a\83X\83g\82Ì\83\8d\83O\82ª\82 \82é\82È\82ç\82»\82ê\82ð\90V\82µ\82¢\83\8a\83X\83g\82É\92Ç\89Á
-      // \8cÃ\82¢\83\8d\83O\82ª\82È\82¯\82ê\82Î\81A\90V\82½\82É\83X\83\8c\83I\83u\83W\83F\83N\83g\82ð\8dì\90¬
-      Board.IntData := 0;
-      Board.BoardPlugIn.EnumThread( DWORD( Board ), @MakeThreadCallBack );
-
-      // \90V\82µ\82¢\83\8a\83X\83g\82É\96³\82©\82Á\82½\83A\83C\83e\83\80\82ð\90V\82µ\82¢\83\8a\83X\83g\82É\92Ç\89Á
-      for i := 0 to Board.Count - 1 do begin
-        if Board.Items[i].IsLogFile then begin
-          Board.IntData := Board.IntData + 1;
-          Board.Items[i].No                                            := Board.IntData;
-          Board.Items[i].AllResCount   := Board.Items[i].Count;
-          Board.Items[i].NewResCount   := 0;
-          Board.Items[i].AgeSage                       := gasNone;
-          Board.ListData.Add( Board.Items[i] );
-        end;
-      end;
-
-      // \8cÃ\82¢\83\8a\83X\83g\82ð\8fÁ\82·\81i\83\8a\83X\83g\82Ì\82Ý\81B\83X\83\8c\83I\83u\83W\83F\83N\83g\8e©\91Ì\82Í\8fÁ\82³\82È\82¢\81j
-      for i := Board.Count - 1 downto 0 do
-        Board.DeleteList( i );
-
-      // \90V\82µ\82¢\83\8a\83X\83g\82ð\83{\81[\83h\83I\83u\83W\83F\83N\83g\82É\92Ç\89Á
-      for i := 0 to Board.ListData.Count - 1 do
-        Board.Add( TThreadItem(Board.ListData[i]) );
-    end else begin
-      //\90V\82µ\82¢\83\8a\83X\83g\82ð\8dì\90¬\82·\82é
-      //\90V\82µ\82¢\83\8a\83X\83g\82É\8cÃ\82¢\83\8a\83X\83g\82Ì\83\8d\83O\82ª\82 \82é\82È\82ç\82»\82ê\82ð\90V\82µ\82¢\83\8a\83X\83g\82É\92Ç\89Á
-      //\8cÃ\82¢\83\8d\83O\82ª\82È\82¯\82ê\82Î\81A\90V\82½\82É\83X\83\8c\83I\83u\83W\83F\83N\83g\82ð\8dì\90¬
-      Body.Text := Content;
-      NumCount := 0;
-      for i := 0 to Body.Count - 1 do begin
-        //if i = 0 then Continue;      //\82P\8ds\96Ú\82Í\83X\83e\81[\83^\83X\8ds\82Ì\82½\82ß\8f\88\97\9d\82È\82µ
-
-        Rec := GikoSys.DivideSubject(Body[i]);
-        Rec.FFileName := Trim(Rec.FFileName);
-        if (Rec.FTitle = '') and (Rec.FCount = 0) then Continue;
-        Inc(NumCount);
-        index := Board.GetIndex(Rec.FFileName);
-        if index = -1 then begin
-          //\90V\82µ\82¢\83X\83\8c\83b\83h
-          NewItem := TThreadItem.Create;
-          NewItem.FileName := Rec.FFileName;
-          NewItem.Title := Rec.FTitle;
-  //                           NewItem.Count := Rec.FCount;
-          NewItem.AllResCount := Rec.FCount;
-          NewItem.ParentBoard := Board;
-          NewItem.No := NumCount;
-          NewItem.RoundDate := ZERO_DATE;
-          NewItem.LastModified := ZERO_DATE;
-          NewItem.AgeSage := gasNew;
-          Board.ListData.Add(NewItem);
-        end else begin
-          //Board.Items[index].Count := Count;
-          //Board.Items[index].Count := Rec.FCount;
-          if Board.Items[index].No > NumCount then
-            Board.Items[index].AgeSage := gasAge
-          else if Board.Items[index].AllResCount < Rec.FCount then
-            Board.Items[index].AgeSage := gasSage
-          else
-            Board.Items[index].AgeSage := gasNone;
-
-
-          Board.Items[index].No := NumCount;
-          Board.Items[index].AllResCount := Rec.FCount;
-  //                           if not Board.Items[index].IsLogFile then
-  //                                   Board.Items[index].Count := Rec.FCount;
-          Board.ListData.Add(Board.Items[index]);
-          Board.DeleteList(index);
-        end;
-      end;
-
-      //\90V\82µ\82¢\83\8a\83X\83g\82É\96³\82©\82Á\82½\83A\83C\83e\83\80\82ð\90V\82µ\82¢\83\8a\83X\83g\82É\92Ç\89Á
-      for i := 0 to Board.Count - 1 do begin
-        if Board.Items[i].IsLogFile then begin
-          inc(NumCount);
-          Board.Items[i].No := NumCount;
-          Board.Items[i].AllResCount := Board.Items[i].Count;
-          Board.Items[i].NewResCount := 0;
-          Board.Items[i].AgeSage := gasNone;
-          Board.ListData.Add(Board.Items[i]);
-        end;
-      end;
-
-      //\8cÃ\82¢\83\8a\83X\83g\82ð\8fÁ\82·\81i\83\8a\83X\83g\82Ì\82Ý\81B\83X\83\8c\83I\83u\83W\83F\83N\83g\8e©\91Ì\82Í\8fÁ\82³\82È\82¢\81j
-      for i := Board.Count - 1 downto 0 do
-        Board.DeleteList(i);
-
-      //\90V\82µ\82¢\83\8a\83X\83g\82ð\83{\81[\83h\83I\83u\83W\83F\83N\83g\82É\92Ç\89Á
-      for i := 0 to Board.ListData.Count - 1 do
-        Board.Add(TThreadItem(Board.ListData[i]));
-
-      //\83\8a\83X\83g(subject.txt)\82ð\95Û\91
-  //           GikoSys.ForceDirectoriesEx(GikoSys.GetLogDir + Board.BBSID);
-  //           Body.SaveToFile(GikoSys.GetSubjectFileName(Board.BBSID));
-      GikoSys.ForceDirectoriesEx(ExtractFilePath(Board.GetSubjectFileName));
-      Body.SaveToFile(Board.GetSubjectFileName);
-       end;
+
+               //dat\97\8e\82¿\83X\83\8c\82Ì\83\\81[\83g\8f\87\82ð\8c\88\92è\82·\82é\82½\82ß\82É\83\\81[\83g\82·\82é
+               if GikoSys.Setting.DatOchiSortIndex >= 0 then begin
+                       Sort.SetSortNoFlag(true);
+                       Sort.SetSortOrder(GikoSys.Setting.DatOchiSortOrder);
+                       Sort.SetSortIndex(GikoSys.Setting.DatOchiSortIndex);
+                       //Sort.SortNonAcquiredCountFlag := GikoSys.Setting.NonAcquiredCount;
+                       Board.CustomSort(ThreadItemSortProc);
+               end;
+
+{$IFDEF DEBUG}
+       rt := GetTickCount - st;
+       Writeln('END Sortd' + IntToStr(rt) + ' ms');
+{$ENDIF}
+
+               for i := Board.Count - 1 downto 0 do
+                       Board.Items[i].AgeSage := gasNull;
+
+               if Board.IsBoardPlugInAvailable then begin
+                       // \90V\82µ\82¢\83\8a\83X\83g\82ð\8dì\90¬\82·\82é
+                       // \90V\82µ\82¢\83\8a\83X\83g\82É\8cÃ\82¢\83\8a\83X\83g\82Ì\83\8d\83O\82ª\82 \82é\82È\82ç\82»\82ê\82ð\90V\82µ\82¢\83\8a\83X\83g\82É\92Ç\89Á
+                       // \8cÃ\82¢\83\8d\83O\82ª\82È\82¯\82ê\82Î\81A\90V\82½\82É\83X\83\8c\83I\83u\83W\83F\83N\83g\82ð\8dì\90¬
+                       Board.IntData := 0;
+{$IFDEF DEBUG}
+       rt := GetTickCount - st;
+       Writeln('Start Enum' + IntToStr(rt) + ' ms');
+{$ENDIF}
+
+                       //\82±\82ê\82ª\92x\82¢\81@\97v\89ü\91P
+                       Board.BeginUpdate;
+                       Board.BoardPlugIn.EnumThread( DWORD( Board ), @MakeThreadCallBack );
+                       Board.EndUpdate;
+
+{$IFDEF DEBUG}
+       rt := GetTickCount - st;
+       Writeln('End Enum' + IntToStr(rt) + ' ms');
+{$ENDIF}
+
+                       //\8cÃ\82¢\83\8a\83X\83g\82É\82µ\82©\82È\82¢\82â\82Â\82ç\82ð\8dí\8f\9c
+                       for i := Board.Count - 1 downto 0 do begin
+                               if( Board.Items[i].AgeSage = gasNull )and not (Board.Items[i].IsLogFile) then
+                                       Board.Delete(i);
+                       end;
+
+                       // \90V\82µ\82¢\83\8a\83X\83g\82É\96³\82©\82Á\82½\83A\83C\83e\83\80\82ð\90V\82µ\82¢\83\8a\83X\83g\82É\92Ç\89Á
+                       for i := 0 to Board.Count - 1 do begin
+                               if(Board.Items[i].AgeSage = gasNull) and (Board.Items[i].IsLogFile) then begin
+                                       Board.IntData := Board.IntData + 1;
+                                       Board.Items[i].No                                               := Board.IntData;
+                                       Board.Items[i].AllResCount      := Board.Items[i].Count;
+                                       Board.Items[i].NewResCount      := 0;
+                                       Board.Items[i].AgeSage                  := gasArch;
+                               end;
+                       end;
+
+               end else begin
+                       //\90V\82µ\82¢\83\8a\83X\83g\82ð\8dì\90¬\82·\82é
+                       //\90V\82µ\82¢\83\8a\83X\83g\82É\8cÃ\82¢\83\8a\83X\83g\82Ì\83\8d\83O\82ª\82 \82é\82È\82ç\82»\82ê\82ð\90V\82µ\82¢\83\8a\83X\83g\82É\92Ç\89Á
+                       //\8cÃ\82¢\83\8d\83O\82ª\82È\82¯\82ê\82Î\81A\90V\82½\82É\83X\83\8c\83I\83u\83W\83F\83N\83g\82ð\8dì\90¬
+                       Body.Text := Content;
+                       NumCount := 0;
+                       for i := 0 to Body.Count - 1 do begin
+                               Rec := GikoSys.DivideSubject(Body[i]);
+                               Rec.FFileName := Trim(Rec.FFileName);
+                               if (Rec.FTitle = '') and (Rec.FCount = 0) then Continue;
+                               Inc(NumCount);
+                               index := Board.GetIndexFromFileName(Rec.FFileName);
+                               if index = -1 then begin
+                                       //\90V\82µ\82¢\83X\83\8c\83b\83h
+                                       NewItem := TThreadItem.Create(
+                                                               nil,
+                                Board,
+                                GikoSys.Get2chBoard2ThreadURL( Board, ChangeFileExt( Rec.FFileName, '' ) ) );
+                                       NewItem.Title := Rec.FTitle;
+                                       NewItem.AllResCount := Rec.FCount;
+                                       NewItem.ParentBoard := Board;
+                                       NewItem.No := NumCount;
+                                       NewItem.RoundDate := ZERO_DATE;
+                                       NewItem.LastModified := ZERO_DATE;
+                                       NewItem.AgeSage := gasNew;
+                                       Board.Add(NewItem);
+                               end else begin
+                                       if Board.Items[index].No > NumCount then
+                                               Board.Items[index].AgeSage := gasAge
+                                       else if Board.Items[index].AllResCount < Rec.FCount then
+                                               Board.Items[index].AgeSage := gasSage
+                                       else
+                                               Board.Items[index].AgeSage := gasNone;
+
+                                       Board.Items[index].No := NumCount;
+                                       Board.Items[index].AllResCount := Rec.FCount;
+                               end;
+                       end;
+                       //\8cÃ\82¢\83\8a\83X\83g\82Ì\8dí\8f\9c
+                       for i := Board.Count - 1 downto 0 do begin
+                               if( Board.Items[i].AgeSage = gasNull )and not (Board.Items[i].IsLogFile) then
+                                       Board.Delete(i);
+                       end;
+
+                       //\90V\82µ\82¢\83\8a\83X\83g\82É\96³\82©\82Á\82½\83A\83C\83e\83\80\82Ì\8dX\90V
+                       for i := 0 to Board.Count - 1 do begin
+                               if( Board.Items[i].AgeSage = gasNull )and (Board.Items[i].IsLogFile) then begin
+                                       inc(NumCount);
+                                       Board.Items[i].No := NumCount;
+                                       Board.Items[i].AllResCount := Board.Items[i].Count;
+                                       Board.Items[i].NewResCount := 0;
+                                       Board.Items[i].AgeSage := gasArch;
+                               end;
+                       end;
+                       //\83\8a\83X\83g(subject.txt)\82ð\95Û\91
+                       GikoSys.ForceDirectoriesEx(ExtractFilePath(Board.GetSubjectFileName));
+                       Body.SaveToFile(Board.GetSubjectFileName);
+               end;
        finally
                Body.Free;
-               Board.ListData.Free;
        end;
+
+
 end;
 
 {procedure TDownloadItem.SaveListFile;
@@ -1135,66 +1185,136 @@ end;
 }
 procedure TDownloadItem.SaveItemFile;
 var
-       Body: TStringList;
+       Body, oldBody: TStringList;
        Cnt: Integer;
        OldCnt: Integer;
        FileName: string;
        ini: TMemIniFile;
        Res: TResRec;
        NewRes: Integer;
+       finish : Boolean;
+       loopCnt : Integer;
+       LastIdx : Integer;
 begin
-       if Trim(Content) = '' then
-               Exit;
        FileName := ThreadItem.GetThreadFileName;
-       GikoSys.ForceDirectoriesEx(ExtractFilePath(FileName));
 
-//     Cnt := 0;
-       Body := TStringList.Create;
-       try
-//             if FileExists(FileName) and (ResponseCode = 206) then begin
-               if FileExists(FileName) and (State = gdsDiffComplete) then begin
-//                     Body.Text := Content;
-//                     if Body.Count > 0 then
-//                             Body.Delete(0);
-//                     Content := Body.Text;
-                       Body.LoadFromFile(FileName);
-                       OldCnt := Body.Count;
-                       Body.Text := Body.Text + Content;
-                       Body.SaveToFile(FileName);
-                       NewRes := Body.Count - OldCnt;
-                       Cnt := Body.Count;
-               end else begin
-                       Body.Text := Content;
-//                     if Body.Count > 0 then
-//                             Body.Delete(0);
-                       Body.SaveToFile(FileName);
+       //if not ThreadItem.IsBoardPlugInAvailable then begin
+    if not ThreadItem.ParentBoard.IsBoardPlugInAvailable then begin
+               if Trim(Content) = '' then
+                       Exit;
 
-                       if ThreadItem.Title = '' then begin
-                               Res := GikoSys.DivideStrLine(Body[0]);
-                               ThreadItem.Title := Res.FTitle;
+               GikoSys.ForceDirectoriesEx(ExtractFilePath(FileName));
+
+               //      Cnt := 0;
+               Body := TStringList.Create;
+               NewRes := 0;
+               OldCnt := 0;
+               try
+               //              if FileExists(FileName) and (ResponseCode = 206) then begin
+                       if FileExists(FileName) and (State = gdsDiffComplete) then begin
+                               loopCnt := 10;
+                               repeat
+                                       finish := true;
+                                       try
+                                               Body.LoadFromFile(FileName);
+                                               OldCnt := Body.Count;
+                                               Body.Text := Body.Text + Content;
+                                               Body.SaveToFile(FileName);
+                                               NewRes := Body.Count - OldCnt;
+                                       except
+                                               on E:EFOpenError do begin
+                                                       sleep(10);
+                                                       Dec(loopCnt);
+                                                       if loopCnt > 0 then
+                                                               finish := false;
+                                               end;
+                                       end;
+                               until finish;
+                               //Cnt := Body.Count;
+                       end else begin
+                               if IsAbone then begin
+                                       // \82 \82Ú\81[\82ñ\82ð\8c\9f\8fo\82µ\82½\82Ì\82Å\82±\82±\82Ü\82Å\93Ç\82ñ\82¾\82Æ\90V\92\85\83\8c\83X\94Ô\82Ì\82Â\82¯\82È\82¨\82µ
+                                       oldBody := TStringList.Create;
+                                       try
+                                               loopCnt := 10;
+                                               repeat
+                                                       finish := true;
+                                                       try
+                                                               oldBody.LoadFromFile(FileName);
+                                                       except
+                                                               on E:EFOpenError do begin
+                                                                       sleep(10);
+                                                                       Dec(loopCnt);
+                                                                       if loopCnt > 0 then
+                                                                               finish := false
+                                                                       else
+                                                                               finish := true;
+                                                               end;
+                                                       end;
+                                               until finish;
+
+                                               Body.Text := Content;
+                                               if (ThreadItem.Kokomade > 0) and (ThreadItem.Kokomade <= oldBody.Count) then begin
+                                                       ThreadItem.Kokomade := Body.IndexOf(oldBody.Strings[ ThreadItem.Kokomade - 1 ]);
+                                                       if ThreadItem.Kokomade <> -1 then ThreadItem.Kokomade := ThreadItem.Kokomade + 1;
+                                               end;
+
+                                               LastIdx := oldBody.Count;
+                                               repeat
+                                                       Dec(LastIdx);
+                                                       OldCnt := Body.IndexOf(oldBody.Strings[ LastIdx ]) + 1;
+                                               until ( OldCnt <> 0 ) or (LastIdx = 0);
+
+                                               if OldCnt >= Body.Count then OldCnt := Body.Count - 1;
+                                               NewRes := Body.Count - OldCnt;
+
+                                               // \82±\82±\82Ü\82Å\93Ç\82ñ\82¾\82ª\90V\92\85\83\8c\83X\94Ô\82ð\92´\82³\82È\82¢\82æ\82¤\82É(\88Ù\8fí\8fI\97¹\8e\9e\82Ì\83\8a\83J\83o\83\8a)
+                                               if ThreadItem.Kokomade > OldCnt then begin
+                                                       if OldCnt > 0 then
+                                                               ThreadItem.Kokomade := OldCnt
+                                                       else
+                                                               ThreadItem.Kokomade := 1;
+                                               end;
+
+                                       finally
+                                               oldBody.Free;
+                                       end;
+
+                               end else begin
+                                       Body.Text := Content;
+                                       //ThreadItem.Count := 0;
+                                       OldCnt := 0;
+                                       NewRes := Body.Count;
+                                       //Cnt := Body.Count;
+                               end;
+       //                      if Body.Count > 0 then
+       //                              Body.Delete(0);
+                               Body.SaveToFile(FileName);
+
+                               if ThreadItem.Title = '' then begin
+                                       THTMLCreate.DivideStrLine(Body[0], @Res);
+                                       ThreadItem.Title := Res.FTitle;
+                               end;
+                               ThreadItem.Size := 0;
                        end;
-                       ThreadItem.Size := 0;
-                       //ThreadItem.Count := 0;
-                       ThreadItem.AllResCount := 0;
-                       ThreadItem.NewResCount := 0;
-                       OldCnt := 0;
-                       NewRes := Body.Count;
                        Cnt := Body.Count;
+               finally
+                       Body.Free;
                end;
-               Cnt := Body.Count;
-       finally
-               Body.Free;
+
+               ThreadItem.Size := ThreadItem.Size + ContentLength;
+               ThreadItem.LastModified := LastModified;
+               ThreadItem.Count := Cnt;
+               //ThreadItem.AllResCount := Cnt;
+               ThreadItem.NewResCount := NewRes;
+               ThreadItem.NewReceive := OldCnt + 1;
        end;
-       ThreadItem.RoundDate := Now;
-       ThreadItem.Size := ThreadItem.Size + ContentLength;
-       ThreadItem.LastModified := LastModified;
-       ThreadItem.Count := Cnt;
-       ThreadItem.AllResCount := Cnt;
-       ThreadItem.NewResCount := NewRes;
+       ThreadItem.AllResCount := ThreadItem.Count;
        ThreadItem.IsLogFile := True;
-       ThreadItem.NewReceive := OldCnt + 1;
-       ThreadItem.UnRead := True;
-       ThreadItem.ParentBoard.UnRead := ThreadItem.ParentBoard.UnRead + 1;
+       ThreadItem.RoundDate := Now;
+       if not ThreadItem.UnRead then begin
+               ThreadItem.UnRead := True;
+       end;
 //     if ThreadItem.RoundNo = 6 then
 //             ThreadItem.RoundNo := 0;
 
@@ -1211,7 +1331,7 @@ begin
                ini.WriteInteger('Setting', 'NewResCount', ThreadItem.NewResCount);
                ini.WriteInteger('Setting', 'NewReceive', ThreadItem.NewReceive);
 //             ini.WriteInteger('Setting', 'RoundNo', ThreadItem.RoundNo);
-               ini.WriteBool('Setting', 'Round', ThreadItem.Round);
+//             ini.WriteBool('Setting', 'Round', ThreadItem.Round);
                ini.WriteBool('Setting', 'UnRead', ThreadItem.UnRead);
                ini.UpdateFile;
        finally