OSDN Git Service

1.52.1.658
[gikonavigoeson/gikonavi.git] / ItemDownload.pas
1 unit ItemDownload;
2
3 interface
4
5 uses
6         Windows, SysUtils, Classes, ComCtrls, Controls, Forms, IdHTTP,
7         {HTTPApp,} YofUtils, IdGlobal, IdException, IdComponent, IniFiles, {DateUtils,}
8         GikoSystem, BoardGroup, MonaUtils, ExternalBoardManager, ExternalBoardPlugInMain,
9         Sort;
10
11 type
12         TDownloadItem = class;
13         TGikoDownloadType = (gdtBoard, gdtThread);
14         TGikoDownloadState = (gdsWait, gdsWork, gdsComplete, gdsDiffComplete, gdsNotModify, gdsAbort, gdsError);
15         TGikoCgiStatus = (gcsOK, gcsINCR, gcsERR);
16         TGikoDLProgress = (gdpStd, gdpAll, gdpDatOchi, gdpOfflaw);
17
18         TGikoWorkEvent = procedure(Sender: TObject; AWorkMode: TWorkMode; const AWorkCount: Integer; ID: Integer) of object;
19         TGikoWorkBeginEvent = procedure(Sender: TObject; AWorkMode: TWorkMode; const AWorkCountMax: Integer; ID: Integer; const AWorkTitle: string) of object;
20         TGikoWorkEndEvent = procedure(Sender: TObject; AWorkMode: TWorkMode; ID: Integer) of object;
21         TDownloadEndEvent = procedure(Sender: TObject; Item: TDownloadItem) of object;
22         TDownloadMsgEvent = procedure(Sender: TObject; Item: TDownloadItem; Msg: string; Icon: TGikoMessageIcon) of object;
23
24         TCgiStatus = record
25                 FStatus: TGikoCgiStatus;
26                 FSize: Integer;
27                 FErrText: string;
28         end;
29
30
31         TDownloadThread = class(TThread)
32         private
33                 FIndy: TIdHttp;
34                 FItem: TDownloadItem;
35                 FNumber: Integer;
36                 FAbort: Boolean;
37                 FMsg: string;
38                 FIcon: TGikoMessageIcon;
39                 FSessionID: string;
40                 FOnWork: TGikoWorkEvent;
41                 FOnWorkBegin: TGikoWorkBeginEvent;
42                 FOnWorkEnd: TGikoWorkEndEvent;
43                 FOnDownloadEnd: TDownloadEndEvent;
44                 FOnDownloadMsg: TDownloadMsgEvent;
45                 FDownloadTitle: string;
46
47                 procedure FireDownloadEnd;
48                 procedure FireDownloadMsg;
49                 procedure GetSessionID;
50                 procedure WorkBegin(Sender: TObject; AWorkMode: TWorkMode; const AWorkCountMax: Integer);
51                 procedure WorkEnd(Sender: TObject; AWorkMode: TWorkMode);
52                 procedure Work(Sender: TObject; AWorkMode: TWorkMode; const AWorkCount: Integer);
53                 function ParseCgiStatus(Content: string): TCgiStatus;
54                 function CgiDownload(ItemType: TGikoDownloadType; URL: string; Modified: TDateTime): Boolean;
55                 function DatDownload(ItemType: TGikoDownloadType; URL: string; Modified: TDateTime; RangeStart: Integer; AdjustLen: Integer): Boolean;
56                 procedure DeleteStatusLine(Item: TDownloadItem);
57                 procedure InitHttpClient(client: TIdHttp);
58                 procedure ClearHttpClient(client: TIdHttp);
59         protected
60                 procedure Execute; override;
61         public
62                 property Item: TDownloadItem read FItem write FItem;
63                 property Number: Integer read FNumber write FNumber;
64                 constructor Create(CreateSuspended: Boolean);
65                                 destructor Destroy; override;
66                 procedure Abort;
67                 property OnWork: TGikoWorkEvent read FOnWork write FOnWork;
68                 property OnWorkBegin: TGikoWorkBeginEvent read FOnWorkBegin write FOnWorkBegin;
69                 property OnWorkEnd: TGikoWorkEndEvent read FOnWorkEnd write FOnWorkEnd;
70                 property OnDownloadEnd: TDownloadEndEvent read FOnDownloadEnd write FOnDownloadEnd;
71                 property OnDownloadMsg: TDownloadMsgEvent read FOnDownloadMsg write FOnDownloadMsg;
72         end;
73
74         TDownloadItem = class(TObject)
75         private
76                 FDownType: TGikoDownloadType;
77                 FBoard: TBoard;
78                 FThreadItem: TThreadItem;
79
80                 FContentLength: Integer;
81                 FLastModified: TDateTime;
82                 FContent: string;
83                 FResponseCode: Smallint;
84                 FState: TGikoDownloadState;
85                 FErrText: string;
86                 FForceDownload: Boolean;
87                 FIsAbone : Boolean;
88         public
89                 procedure SaveListFile;
90                 procedure SaveItemFile;
91
92                 property DownType: TGikoDownloadType read FDownType write FDownType;
93                 property Board: TBoard read FBoard write FBoard;
94                 property ThreadItem: TThreadItem read FThreadItem write FThreadItem;
95
96                 property ContentLength: Integer read FContentLength write FContentLength;
97                 property LastModified: TDateTime read FLastModified write FLastModified;
98                 property Content: string read FContent write FContent;
99                 property ResponseCode: Smallint read FResponseCode write FResponseCode;
100                 property State: TGikoDownloadState read FState write FState;
101                 property ErrText: string read FErrText write FErrText;
102                 property ForceDownload: Boolean read FForceDownload write FForceDownload;
103                 property IsAbone : Boolean read FIsAbone write FIsAbone;
104         end;
105
106 implementation
107
108 uses
109         Y_TextConverter, MojuUtils, HTMLCreate;
110
111 constructor TDownloadThread.Create(CreateSuspended: Boolean);
112 begin
113         inherited Create(CreateSuspended);
114         FIndy := TIdHttp.Create(nil);
115
116         FIndy.OnWorkBegin := WorkBegin;
117         FIndy.OnWorkEnd := WorkEnd;
118         FIndy.OnWork := Work;
119 end;
120
121 destructor TDownloadThread.Destroy;
122 begin
123         ClearHttpClient(FIndy);
124         FIndy.Free;
125         inherited;
126 end;
127
128 function RFC1123_Date(aDate : TDateTime) : String;
129 const
130          StrWeekDay : String = 'MonTueWedThuFriSatSun';
131          StrMonth        : String = 'JanFebMarAprMayJunJulAugSepOctNovDec';
132 var
133          Year, Month, Day                        : Word;
134          Hour, Min,      Sec, MSec : Word;
135          DayOfWeek                                                      : Word;
136 begin
137          DecodeDate(aDate, Year, Month, Day);
138          DecodeTime(aDate, Hour, Min,    Sec, MSec);
139          DayOfWeek := ((Trunc(aDate) - 2) mod 7);
140          Result := Copy(StrWeekDay, 1 + DayOfWeek * 3, 3) + ', ' +
141                                                  Format('%2.2d %s %4.4d %2.2d:%2.2d:%2.2d',
142                                                                                 [Day, Copy(StrMonth, 1 + 3 * (Month - 1), 3),
143                                                                                  Year, Hour, Min, Sec]);
144 end;
145 // ******************************************************************
146 // HTTPClient\82Ì\8f\89\8aú\89»
147 // ******************************************************************
148 procedure TDownloadThread.InitHttpClient(client: TIdHttp);
149 begin
150         ClearHttpClient(client);
151         client.Disconnect;
152         client.Request.UserAgent := GikoSys.GetUserAgent;
153         client.RecvBufferSize := Gikosys.Setting.RecvBufferSize;
154         client.ProxyParams.BasicAuthentication := False;
155         client.ReadTimeout := GikoSys.Setting.ReadTimeOut;
156         {$IFDEF DEBUG}
157         Writeln('------------------------------------------------------------');
158         {$ENDIF}
159         //FIndy.AllowCookies := False;
160         if GikoSys.Setting.ReadProxy then begin
161                 if GikoSys.Setting.ProxyProtocol then
162                         client.ProtocolVersion := pv1_1
163                 else
164                         client.ProtocolVersion := pv1_0;
165                 client.ProxyParams.ProxyServer := GikoSys.Setting.ReadProxyAddress;
166                 client.ProxyParams.ProxyPort := GikoSys.Setting.ReadProxyPort;
167                 client.ProxyParams.ProxyUsername := GikoSys.Setting.ReadProxyUserID;
168                 client.ProxyParams.ProxyPassword := GikoSys.Setting.ReadProxyPassword;
169                 if GikoSys.Setting.ReadProxyUserID <> '' then
170                         client.ProxyParams.BasicAuthentication := True;
171                 {$IFDEF DEBUG}
172                 Writeln('\83v\83\8d\83L\83V\90Ý\92è\82 \82è');
173                 Writeln('\83z\83X\83g: ' + GikoSys.Setting.ReadProxyAddress);
174                 Writeln('\83|\81[\83g: ' + IntToStr(GikoSys.Setting.ReadProxyPort));
175                 {$ENDIF}
176         end else begin
177                 if GikoSys.Setting.Protocol then
178                         client.ProtocolVersion := pv1_1
179                 else
180                         client.ProtocolVersion := pv1_0;
181                 client.ProxyParams.ProxyServer := '';
182                 client.ProxyParams.ProxyPort := 80;
183                 client.ProxyParams.ProxyUsername := '';
184                 client.ProxyParams.ProxyPassword := '';
185                 {$IFDEF DEBUG}
186                 Writeln('\83v\83\8d\83L\83V\90Ý\92è\82È\82µ');
187                 {$ENDIF}
188         end;
189 end;
190 // ******************************************************************
191 // HTTPClient\82Ì\83\8a\83N\83G\83X\83g\82Æ\83\8c\83X\83|\83\93\83X\82Ì\83f\81[\83^\82Ì\8fÁ\8b\8e
192 // ******************************************************************
193 procedure TDownloadThread.ClearHttpClient(client: TIdHttp);
194 begin
195         client.Request.CustomHeaders.Clear;
196         client.Request.RawHeaders.Clear;
197         client.Request.Clear;
198         client.Response.CustomHeaders.Clear;
199         client.Response.RawHeaders.Clear;
200         client.Response.Clear;
201
202         client.ProxyParams.Clear;
203 end;
204 procedure TDownloadThread.Execute;
205 var
206         ResStream: TMemoryStream;
207
208         URL: string;
209         CgiStatus: TCgiStatus;
210         Modified: TDateTime;
211         RangeStart: Integer;
212         AdjustLen: Integer;
213         Idx: Integer;
214         ATitle: string;
215         DownloadResult: Boolean;
216         boardPlugIn     : TBoardPlugIn;
217         lastContent             : string;
218         logFile                         : TFileStream;
219         adjustMargin    : Integer;
220 const
221         ADJUST_MARGIN   = 16;
222 begin
223         while not Terminated do begin
224                 //===== \83v\83\89\83O\83C\83\93
225                 FAbort := False;
226                 boardPlugIn := nil;
227                 ExternalBoardManager.OnWork                             := Work;
228                 ExternalBoardManager.OnWorkBegin        := WorkBegin;
229                 ExternalBoardManager.OnWorkEnd          := WorkEnd;
230
231                 FDownloadTitle := '';
232                 case FItem.FDownType of
233                 gdtBoard:
234                         begin
235                                 FDownloadTitle := FItem.FBoard.Title;
236                                 if FItem.FBoard <> nil then begin
237                                         if FItem.FBoard.IsBoardPlugInAvailable then begin
238                                                 boardPlugIn     := FItem.FBoard.BoardPlugIn;
239                                                 Item.State      := TGikoDownloadState( boardPlugIn.DownloadBoard( DWORD( FItem.FBoard ) ) );
240                                         end;
241                                 end;
242                         end;
243                 gdtThread:
244                         begin
245                                 FDownloadTitle := FItem.FThreadItem.Title;
246                                 if FItem.FThreadItem <> nil then begin
247                                         if FItem.FThreadItem.ParentBoard.IsBoardPlugInAvailable then begin
248                                                 boardPlugIn := FItem.FThreadItem.ParentBoard.BoardPlugIn;
249                                                 Item.State      := TGikoDownloadState( boardPlugIn.DownloadThread( DWORD( FItem.FThreadItem ) ) );
250                                         end;
251                                         //if FItem.FThreadItem.IsBoardPlugInAvailable then begin
252                                         //      boardPlugIn     := FItem.FThreadItem.BoardPlugIn;
253                                         //      Item.State      := TGikoDownloadState( boardPlugIn.DownloadThread( DWORD( FItem.FThreadItem ) ) );
254                                         //end;
255                                 end;
256                         end;
257                 end;
258                 if Length(FDownloadTitle) = 0 then
259                         FDownloadTitle := '\81i\96¼\8fÌ\95s\96¾\81j';
260
261                 if boardPlugIn <> nil then begin
262                         if FAbort then begin
263                                 Item.State := gdsAbort;
264                         end;
265                         if Assigned( OnDownloadEnd ) then
266                                 Synchronize( FireDownloadEnd );
267                         if Terminated then
268                                 Break;
269
270                         Suspend;
271                         Continue;
272                 end;
273
274                 FAbort := False;
275                 //===== \83v\83\89\83O\83C\83\93\82ð\8eg\97p\82µ\82È\82¢\8fê\8d\87
276                 InitHttpClient(FIndy);
277                 adjustMargin := 0;
278                 if Item.DownType = gdtThread then begin
279                         if FileExists( Item.ThreadItem.GetThreadFileName ) then begin
280                                 // dat \83t\83@\83C\83\8b\82Ì\8dÅ\8cã\82ð\93Ç\82Ý\8fo\82·
281                                 SetLength( lastContent, ADJUST_MARGIN + 1 );
282                                 logFile := TFileStream.Create( Item.ThreadItem.GetThreadFileName, fmOpenRead or fmShareDenyWrite );
283                                 try
284                                         logFile.Seek( -(ADJUST_MARGIN + 1), soFromEnd );
285                                         logFile.Read( lastContent[ 1 ], ADJUST_MARGIN + 1 );
286                                         lastContent := StringReplace( lastContent, #13, '', [] );       // CR \82Ì\8dí\8f\9c
287                                 finally
288                                         logFile.Free;
289                                 end;
290                         end else begin
291         lastContent := '';
292                         end;
293                         adjustMargin := Length( lastContent );
294                 end;
295
296                 FIndy.Request.ContentRangeStart := 0;
297                 FIndy.Request.LastModified := ZERO_DATE;
298                 ResStream := TMemoryStream.Create;
299                 try
300                         try
301                                 //********************
302                                 //DAT or Subject\8eæ\93¾
303                                 //********************
304                                 Item.ResponseCode := 0;
305                                 RangeStart := 0;
306                                 AdjustLen := 0;
307                                 Modified := 0;
308                                 if Item.DownType = gdtBoard then begin
309                                         {$IFDEF DEBUG}
310                                         Writeln('Subject\8eæ\93¾');
311                                         Writeln('URL: ' + Item.Board.GetReadCgiURL);
312                                         Writeln('Modified: ' + FloatToStr(Item.Board.LastModified));
313                                         {$ENDIF}
314                                         URL := Item.Board.GetReadCgiURL;
315                                         if Item.ForceDownload then begin
316                                                 // \8b­\90§\8eæ\93¾
317                                                 ATitle := Item.Board.Title;
318                                                 if ATitle = '' then
319                                                         ATitle := '\81i\96¼\8fÌ\95s\96¾\81j';
320                                                 FMsg := '\81\9a\8b­\90§\8eæ\93¾\82ð\8ds\82¢\82Ü\82· - [' + ATitle + ']';
321                                                 FIcon := gmiWhat;
322                                                 if Assigned(OnDownloadMsg) then
323                                                         Synchronize(FireDownloadMsg);
324                                                 Modified := ZERO_DATE
325                                         end else begin
326                                                 Modified := Item.Board.LastModified;
327                                         end;
328                                 end else if Item.DownType = gdtThread then begin
329                                         {$IFDEF DEBUG}
330                                         Writeln('DAT\8eæ\93¾');
331                                         Writeln('URL: ' + Item.ThreadItem.GetDatURL);
332                                         Writeln('Modified: ' + FloatToStr(Item.ThreadItem.LastModified));
333                                         {$ENDIF}
334                                         URL := Item.ThreadItem.GetDatURL;
335                                         if Item.ForceDownload then begin
336                                                 // \8b­\90§\8eæ\93¾
337                                                 ATitle := Item.ThreadItem.Title;
338                                                 if ATitle = '' then
339                                                         ATitle := '\81i\96¼\8fÌ\95s\96¾\81j';
340                                                 FMsg := '\81\9a\8b­\90§\8eæ\93¾\82ð\8ds\82¢\82Ü\82· - [' + ATitle + ']';
341                                                 FIcon := gmiWhat;
342                                                 if FileExists(ChangeFileExt(Item.FThreadItem.GetThreadFileName,'.NG')) = true then
343                                                         DeleteFile(ChangeFileExt(Item.FThreadItem.GetThreadFileName,'.NG'));
344                                                 if Assigned(OnDownloadMsg) then
345                                                         Synchronize(FireDownloadMsg);
346                                                 Modified := ZERO_DATE;
347                                                 RangeStart := 0;
348                                                 AdjustLen := 0;
349                                         end else begin
350                                                 Modified := Item.ThreadItem.LastModified;
351                                                 if Item.ThreadItem.Size > 0 then begin
352                                                         {$IFDEF DEBUG}
353                                                         Writeln('RangeStart: ' + IntToStr(Item.ThreadItem.Size));
354                                                         {$ENDIF}
355                                                         // \82 \82Ú\81[\82ñ\83`\83F\83b\83N\82Ì\82½\82ß adjustMargin \83o\83C\83g\91O\82©\82ç\8eæ\93¾
356                                                         RangeStart := Item.ThreadItem.Size;
357                                                         AdjustLen := -adjustMargin;
358                                                 end;
359                                         end;
360                                 end;
361                                 Item.IsAbone := False;
362                                 DownloadResult := DatDownload(Item.DownType, URL, Modified, RangeStart, AdjustLen);
363                                 {$IFDEF DEBUG}
364                                 Writeln('ResponseCode: ' + IntToStr(FIndy.ResponseCode));
365                                 {$ENDIF}
366                                 if Item.DownType = gdtThread then begin
367                                         if Item.ResponseCode = 416 then begin
368                                                 Item.IsAbone := True;
369                                                 DownloadResult := True;
370                                         end else if DownloadResult and (AdjustLen < 0) then begin
371                                                 if Copy( Item.Content, 1, adjustMargin ) <> lastContent then
372                                                         Item.IsAbone := True;
373                                         end;
374                                 end;
375
376                                 if Trim(FIndy.Response.RawHeaders.Values['Date']) <> '' then begin
377                                         if Item.DownType = gdtBoard then
378                                                 Item.Board.LastGetTime := MonaUtils.DateStrToDateTime(FIndy.Response.RawHeaders.Values['Date'])
379                                         else
380                                                 Item.ThreadItem.ParentBoard.LastGetTime := MonaUtils.DateStrToDateTime(FIndy.Response.RawHeaders.Values['Date']);
381                                 end;
382
383                                 if DownloadResult then begin
384                                         {$IFDEF DEBUG}
385                                         Writeln('Date:' + FIndy.Response.RawHeaders.Values['Date']);
386                                         {$ENDIF}
387                                         if Item.IsAbone then begin
388                                                 {$IFDEF DEBUG}
389                                                 Writeln('\82 \82Ú\81[\82ñ\8c\9f\8fo');
390                                                 {$ENDIF}
391                                                 ATitle := Item.ThreadItem.Title;
392                                                 if ATitle = '' then
393                                                         ATitle := '\81i\96¼\8fÌ\95s\96¾\81j';
394                                                 //\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¾
395                                                 RangeStart := 0;
396                                                 AdjustLen := 0;
397                                                 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 + ']';
398                                                 FIcon := gmiWhat;
399                                                 if FileExists(ChangeFileExt(Item.FThreadItem.GetThreadFileName,'.NG')) = true then
400                                                         DeleteFile(ChangeFileExt(Item.FThreadItem.GetThreadFileName,'.NG'));
401                                                 if Assigned(OnDownloadMsg) then
402                                                         Synchronize(FireDownloadMsg);
403                                                 if not DatDownload(Item.DownType, URL, ZERO_DATE, RangeStart, AdjustLen) then
404                                                         Item.State := gdsError;
405                                                 {$IFDEF DEBUG}
406                                                 Writeln('\82 \82Ú\81[\82ñ\8dÄ\8eæ\93¾\8cã');
407                                                 Writeln('ResponseCode: ' + IntToStr(Item.ResponseCode));
408                                                 {$ENDIF}
409                                         end else if (Item.DownType = gdtThread) and (AdjustLen < 0) then begin
410                                                 // \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
411                                                 Item.Content := Copy(Item.Content, adjustMargin + 1, MaxInt);
412                                         end;
413                                 end else begin
414                                         Item.State := gdsError;
415                                         if (Item.DownType = gdtBoard) and (Item.ResponseCode = 302) then begin
416                                                 FMsg := '\81\9a\81\9a\94Â\82ª\88Ú\93]\82µ\82½\82©\82à\82µ\82ê\82È\82¢\82Ì\82Å\94Â\8dX\90V\82ð\8ds\82Á\82Ä\82­\82¾\82³\82¢\81\9a\81\9a';
417                                                 FIcon := gmiNG;
418                                                 if Assigned(OnDownloadMsg) then
419                                                         Synchronize(FireDownloadMsg);
420                                         end;
421                                 end;
422
423                                 //********************
424                                 //dat.gz\8eæ\93¾(1)
425                                 //********************
426                                 if (Item.DownType = gdtThread) and (Item.ResponseCode = 302) then begin
427                                         {$IFDEF DEBUG}
428                                         Writeln('dat.gz\8eæ\93¾');
429                                         {$ENDIF}
430                                         ATitle := Item.ThreadItem.Title;
431                                         if ATitle = '' then
432                                                 ATitle := '\81i\96¼\8fÌ\95s\96¾\81j';
433                                         FMsg := '\81\9adat\82ª\91\8dÝ\82µ\82È\82¢\82½\82ß\89ß\8b\8e\83\8d\83O(dat.gz)\82ð\92T\82µ\82Ü\82· - [' + ATitle + ']';
434                                         FIcon := gmiWhat;
435                                         if Assigned(OnDownloadMsg) then
436                                                 Synchronize(FireDownloadMsg);
437                                         URL := Item.ThreadItem.GetDatgzURL;
438                                         Modified := Item.ThreadItem.LastModified;
439                                         RangeStart := 0;
440                                         AdjustLen := 0;
441                                         if not DatDownload(Item.DownType, URL, Modified, RangeStart, AdjustLen) then
442                                                 Item.State := gdsError;
443                                         {$IFDEF DEBUG}
444                                         Writeln('ResponseCode: ' + IntToStr(Item.ResponseCode));
445                                         {$ENDIF}
446                                 end;
447
448                                 //********************
449                                 //dat.gz\81@\81¨\81@dat\82Ì\8eæ\93¾\81@2005\94N6\8c\8e\92Ç\89Á\81@by\82à\82\82ã
450                                 //********************
451                                 if (Item.DownType = gdtThread) and (Item.ResponseCode = 302) then begin
452                                         {$IFDEF DEBUG}
453                                         Writeln('dat\8eæ\93¾');
454                                         {$ENDIF}
455                                         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 + ']';
456                                         FIcon := gmiWhat;
457                                         if Assigned(OnDownloadMsg) then
458                                                 Synchronize(FireDownloadMsg);
459                                         URL := ChangeFileExt(URL, '');
460                                         Modified := Item.ThreadItem.LastModified;
461                                         RangeStart := 0;
462                                         AdjustLen := 0;
463                                         if not DatDownload(Item.DownType, URL, Modified, RangeStart, AdjustLen) then
464                                                 Item.State := gdsError;
465                                         {$IFDEF DEBUG}
466                                         Writeln('ResponseCode: ' + IntToStr(Item.ResponseCode));
467                                         {$ENDIF}
468                                 end;
469
470                                 //********************
471                                 //dat.gz\8eæ\93¾(2)
472                                 //********************
473 {
474                                 if (Item.DownType = gdtThread) and (Item.ResponseCode = 302) then begin
475                                         ATitle := Item.ThreadItem.Title;
476                                         if ATitle = '' then
477                                                 ATitle := '\81i\96¼\8fÌ\95s\96¾\81j';
478                                         FMsg := '\81\9a\89ß\8b\8e\83\8d\83O(1)\82ª\91\8dÝ\82µ\82È\82¢\82½\82ß\89ß\8b\8e\83\8d\83O(2)\82©\82ç\92T\82µ\82Ü\82· - [' + ATitle + ']';
479                                         FIcon := gmiWhat;
480                                         if Assigned(OnDownloadMsg) then
481                                                 Synchronize(FireDownloadMsg);
482                                         URL := Item.ThreadItem.GetOldDatgzURL;
483                                         Modified := Item.ThreadItem.LastModified;
484                                         RangeStart := 0;
485                                         AdjustLen := 0;
486                                         if not DatDownload(Item.DownType, URL, Modified, RangeStart, AdjustLen) then
487                                                 Item.State := gdsError;
488                                 end;
489 }
490
491                                 //********************
492                                 //offlaw.cgi\82Å\8eæ\93¾
493                                 //********************
494                                 FSessionID := '';
495                                 Synchronize(GetSessionID);
496                                 if (Item.DownType = gdtThread) and (Item.ResponseCode = 302) and (FSessionID <> '') then begin
497                                         {$IFDEF DEBUG}
498                                         Writeln('offlaw.cgi\82Å\8eæ\93¾');
499                                         {$ENDIF}
500                                         ATitle := Item.ThreadItem.Title;
501                                         if ATitle = '' then
502                                                 ATitle := '\81i\96¼\8fÌ\95s\96¾\81j';
503                                         FMsg := '\81\9adat.gz\82ª\91\8dÝ\82µ\82È\82¢\82½\82ßofflaw.cgi\82ð\97\98\97p\82µ\82Ü\82· - [' + ATitle + ']';
504                                         FIcon := gmiWhat;
505                                         if Assigned(OnDownloadMsg) then
506                                                 Synchronize(FireDownloadMsg);
507                                         URL := Item.ThreadItem.GetOfflawCgiURL(FSessionID);
508                                         Modified := Item.ThreadItem.LastModified;
509                                         RangeStart := 0;
510                                         AdjustLen := 0;
511                                         if not DatDownload(Item.DownType, URL, Modified, RangeStart, AdjustLen) then begin
512                                                 {$IFDEF DEBUG}
513                                                 Writeln('ResponseCode: ' + IntToStr(Item.ResponseCode));
514                                                 {$ENDIF}
515                                                 Item.State := gdsError;
516
517                                                 if (Item.DownType = gdtThread) and (Item.ResponseCode = 302) then begin
518                                                         FMsg := '\94Â\82ª\88Ú\93]\82µ\82½\82©\82à\82µ\82ê\82È\82¢\82Ì\82Å\94Â\8dX\90V\82ð\8ds\82Á\82Ä\82­\82¾\82³\82¢\81B';
519                                                         FIcon := gmiNG;
520                                                         if Assigned(OnDownloadMsg) then
521                                                                 Synchronize(FireDownloadMsg);
522                                                 end;
523
524                                         end else begin
525                                                 CgiStatus := ParseCgiStatus(Item.Content);
526                                                 {$IFDEF DEBUG}
527                                                 Writeln('ResponseCode: ' + IntToStr(Item.ResponseCode));
528                                                 {$ENDIF}
529                                                 case CgiStatus.FStatus of
530                                                         gcsOK: begin
531                                                                 {$IFDEF DEBUG}
532                                                                 Writeln('CGIStatus: OK');
533                                                                 {$ENDIF}
534                                                                 Item.ResponseCode := 200;
535                                                                 DeleteStatusLine(Item);
536                                                         end;
537                                                         gcsINCR: begin
538                                                                 //\8d¡\82Í\82 \82è\82¦\82È\82¢
539                                                                 {$IFDEF DEBUG}
540                                                                 Writeln('CGIStatus: 206');
541                                                                 {$ENDIF}
542                                                                 Item.ResponseCode := 206;
543                                                                 DeleteStatusLine(Item);
544                                                         end;
545                                                         gcsERR: begin
546                                                                 {$IFDEF DEBUG}
547                                                                 Writeln('CGIStatus: 404(ERROR)');
548                                                                 {$ENDIF}
549                                                                 Item.ResponseCode := 404;
550                                                                 Item.State := gdsError;
551                                                                 Item.ErrText := CgiStatus.FErrText;
552                                                         end;
553                                                 end;
554                                                 if (Item.ResponseCode = 404) and (AnsiPos('\89ß\8b\8e\83\8d\83O\91q\8cÉ\82Å\94­\8c©', Item.ErrText) <> 0) then begin
555                                                         {$IFDEF DEBUG}
556                                                         Writeln('\89ß\8b\8e\83\8d\83O\8eæ\93¾');
557                                                         {$ENDIF}
558                                                         ATitle := Item.ThreadItem.Title;
559                                                         if ATitle = '' then
560                                                                 ATitle := '\81i\96¼\8fÌ\95s\96¾\81j';
561                                                         FMsg := '\81\9a\89ß\8b\8e\83\8d\83O\91q\8cÉ\82Å\94­\8c© - [' + ATitle + ']';
562                                                         FIcon := gmiWhat;
563                                                         if Assigned(OnDownloadMsg) then
564                                                                 Synchronize(FireDownloadMsg);
565                                                         Idx := Pos(' ', Item.ErrText);
566                                                         if Idx <> 0 then begin
567                                                                 URL := Copy(Item.ErrText, Idx + 1, Length(Item.ErrText));
568                                                                 if Pos( '://', URL ) = 0 then begin
569                                                                         if Pos('../', URL) = 1 then
570                                                                                 URL := Copy(URL, 4, MaxInt );
571                                                                         if Pos( '/', URL ) = 1 then
572                                                                                 URL := Copy( URL, 2, MaxInt );
573                                                                         URL := GikoSys.UrlToServer(Item.ThreadItem.ParentBoard.URL) + URL;
574                                                                 end;
575                                                                 Modified := Item.ThreadItem.LastModified;
576                                                                 RangeStart := 0;
577                                                                 AdjustLen := 0;
578                                                                 if not DatDownload(Item.DownType, URL, Modified, RangeStart, AdjustLen) then
579                                                                         Item.State := gdsError;
580                                                                 {$IFDEF DEBUG}
581                                                                 Writeln('ResponseCode: ' + IntToStr(Item.ResponseCode));
582                                                                 {$ENDIF}
583                                                         end;
584                                                 end;
585                                         end;
586                                 end else begin
587                                         if (Item.DownType = gdtThread) and (Item.ResponseCode = 302) and (FSessionID = '') then begin
588                                                 {$IFDEF DEBUG}
589                                                 Writeln('\83\8d\83O\83C\83\93\82³\82ê\82Ä\82È\82¢\82Ì\82Å\89ß\8b\8e\83\8d\83O\8eæ\93¾\95s\89Â');
590                                                 {$ENDIF}
591                                                 ATitle := Item.ThreadItem.Title;
592                                                 if ATitle = '' then
593                                                         ATitle := '\81i\96¼\8fÌ\95s\96¾\81j';
594                                                 FMsg := '\81\9a\83\8d\83O\83C\83\93\82³\82ê\82Ä\82¢\82È\82¢\82½\82ß\92T\82·\82±\82Æ\82ª\8fo\97\88\82Ü\82¹\82ñ - [' + ATitle + ']';
595                                                 FIcon := gmiSAD;
596                                                 if Assigned(OnDownloadMsg) then
597                                                         Synchronize(FireDownloadMsg);
598                                         end;
599                                 end;
600
601                                 case Item.ResponseCode of
602                                         200: Item.State := gdsComplete;
603                                         206: Item.State := gdsDiffComplete;
604                                         304: Item.State := gdsNotModify;
605                                         else
606                                                 Item.State := gdsError;
607                                 end;
608                         except
609                                 Item.State := gdsError;
610                         end;
611                         if FAbort then
612                                 Item.State := gdsAbort;
613                 finally
614                         if Assigned(OnDownloadEnd) then
615                                 Synchronize(FireDownloadEnd);
616                         ResStream.Free;
617                 end;
618
619                 ClearHttpClient(FIndy);
620
621                 if Terminated then Break;
622                 Suspend;
623         end;
624 end;
625
626 function TDownloadThread.CgiDownload(ItemType: TGikoDownloadType; URL: string; Modified: TDateTime): Boolean;
627 var
628         ResponseCode: Integer;
629         ResStream: TMemoryStream;
630 begin
631         ResponseCode := -1;
632         FIndy.Request.ContentRangeStart := 0;
633         FIndy.Request.ContentRangeEnd := 0;
634
635         FIndy.Request.CustomHeaders.Clear;
636         if (Modified <> 0) and (Modified <> ZERO_DATE) then begin
637                 FIndy.Request.LastModified := modified - OffsetFromUTC;
638         end;
639         FIndy.Request.AcceptEncoding := '';
640         FIndy.Request.Accept := 'text/html';
641         ResStream := TMemoryStream.Create;
642         try
643                 try
644                         ResStream.Clear;
645                         {$IFDEF DEBUG}
646                         Writeln('URL: ' + URL);
647                         {$ENDIF}
648                         FIndy.Get(URL, ResStream);
649                         Item.Content := GikoSys.GzipDecompress(ResStream, FIndy.Response.ContentEncoding);
650                         Item.LastModified := FIndy.Response.LastModified;
651                         //\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é
652                         Item.ContentLength := Length(Item.Content);
653                         //\96³\82¢\82Æ\8ev\82¤\82¯\82Ç\81B\81B\81B
654                         if Item.Content = '' then
655                                 Result := False
656                         else
657                                 Result := True;
658                         {$IFDEF DEBUG}
659                         Writeln('\8eæ\93¾\82Å\97á\8aO\82È\82µ');
660                         {$ENDIF}
661                         ResponseCode := FIndy.ResponseCode;
662                 except
663                         on E: EIdSocketError do begin
664                                 Item.Content := '';
665                                 Item.LastModified := ZERO_DATE;
666                                 Item.ContentLength := 0;
667                                 Item.ErrText := E.Message;
668                                 Result := False;
669                                 ResponseCode := -1;
670                                 Screen.Cursor := crDefault;
671                         end;
672                         on E: EIdConnectException do begin
673                                 Item.Content := '';
674                                 Item.LastModified := ZERO_DATE;
675                                 Item.ContentLength := 0;
676                                 Item.ErrText := E.Message;
677                                 Result := False;
678                                 ResponseCode := -1;
679                                 Screen.Cursor := crDefault;
680                         end;
681                         on E: Exception do begin
682                                 {$IFDEF DEBUG}
683                                 Writeln('\8eæ\93¾\82Å\97á\8aO\82 \82è');
684                                 Writeln('E.Message: ' + E.Message);
685                                 {$ENDIF}
686                                 Item.Content := '';
687                                 Item.LastModified := ZERO_DATE;
688                                 Item.ContentLength := 0;
689                                 Item.ErrText := E.Message;
690                                 ResponseCode := FIndy.ResponseCode;
691                                 Result := False;
692                                 Screen.Cursor := crDefault;
693                         end;
694                 end;
695         finally
696                 if (Item.ContentLength = 0) and (ResponseCode = 206) then
697                         Item.ResponseCode := 304
698                 else
699                         Item.ResponseCode := ResponseCode;
700                 ResStream.Free;
701         end;
702 end;
703
704 function TDownloadThread.DatDownload(ItemType: TGikoDownloadType; URL: string; Modified: TDateTime; RangeStart: Integer; AdjustLen: Integer): Boolean;
705 var
706         ResponseCode: Integer;
707         ResStream: TMemoryStream;
708 begin
709         ResponseCode := -1;
710         if (ItemType = gdtThread) and (RangeStart > 0) then begin
711                 FIndy.Request.ContentRangeStart := RangeStart + AdjustLen;
712                 FIndy.Request.ContentRangeEnd := 0;
713         end else begin
714                 FIndy.Request.ContentRangeStart := 0;
715                 FIndy.Request.ContentRangeEnd := 0;
716         end;
717
718         FIndy.Request.CustomHeaders.Clear;
719         FIndy.Request.CacheControl := 'no-cache';
720         FIndy.Request.CustomHeaders.Add('Pragma: no-cache');
721         if (Modified <> 0) and (Modified <> ZERO_DATE) then begin
722                 FIndy.Request.LastModified := modified - OffsetFromUTC;
723         end;
724         if RangeStart = 0 then
725                 FIndy.Request.AcceptEncoding := 'gzip'
726         else
727                 FIndy.Request.AcceptEncoding := '';
728         ResStream := TMemoryStream.Create;
729         try
730                 try
731                         ResStream.Clear;
732                         {$IFDEF DEBUG}
733                         Writeln('URL: ' + URL);
734                         {$ENDIF}
735                         FIndy.Get(URL, ResStream);
736             Item.Content := GikoSys.GzipDecompress(ResStream, FIndy.Response.ContentEncoding);
737                         Item.LastModified := FIndy.Response.LastModified;
738                         //\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é
739 //                      Item.ContentLength := FIndy.Response.ContentLength + AdjustLen;
740                         Item.ContentLength := Length(Item.Content) + AdjustLen;
741                         //\96³\82¢\82Æ\8ev\82¤\82¯\82Ç\81B\81B\81B
742 //                      if (FIndy.ResponseCode in [200, 206]) and (Item.Content = '') then
743 //                              Result := False
744                         if Item.Content = '' then
745                                 Result := False
746                         else
747                                 Result := True;
748                         {$IFDEF DEBUG}
749                         Writeln('\8eæ\93¾\82Å\97á\8aO\82È\82µ');
750                         {$ENDIF}
751                         ResponseCode := FIndy.ResponseCode;
752                 except
753                         on E: EIdSocketError do begin
754                                 Item.Content := '';
755                                 Item.LastModified := ZERO_DATE;
756                                 Item.ContentLength := 0;
757                                 Item.ErrText := E.Message;
758                                 Result := False;
759                                 ResponseCode := -1;
760                                 Screen.Cursor := crDefault;
761                         end;
762                         on E: EIdConnectException do begin
763                                 Item.Content := '';
764                                 Item.LastModified := ZERO_DATE;
765                                 Item.ContentLength := 0;
766                                 Item.ErrText := E.Message;
767                                 Result := False;
768                                 ResponseCode := -1;
769                                 Screen.Cursor := crDefault;
770                         end;
771                         on E: Exception do begin
772                                 {$IFDEF DEBUG}
773                                 Writeln('\8eæ\93¾\82Å\97á\8aO\82 \82è');
774                                 Writeln('E.Message: ' + E.Message);
775                                 {$ENDIF}
776                                 Item.Content := '';
777                                 Item.LastModified := ZERO_DATE;
778                                 Item.ContentLength := 0;
779                                 Item.ErrText := E.Message;
780                                 ResponseCode := FIndy.ResponseCode;
781                                 Result := False;
782                                 Screen.Cursor := crDefault;
783                         end;
784                 end;
785         finally
786                 if (Item.ContentLength = 0) and (ResponseCode = 206) then
787                         Item.ResponseCode := 304
788                 else
789                         Item.ResponseCode := ResponseCode;
790                 ResStream.Free;
791         end;
792 end;
793
794 procedure TDownloadThread.FireDownloadEnd;
795 begin
796         OnDownloadEnd(self, Item);
797 end;
798
799 procedure TDownloadThread.FireDownloadMsg;
800 begin
801         OnDownloadMsg(Self, Item, FMsg, FIcon);
802 end;
803
804 procedure TDownloadThread.GetSessionID;
805 begin
806         FSessionID := GikoSys.Dolib.SessionID;
807 end;
808
809 procedure TDownloadThread.Abort;
810 begin
811         FAbort := True;
812         FIndy.DisconnectSocket;
813         if socket <> nil then begin
814                 socket.DisconnectSocket;
815         end;
816 end;
817
818 procedure TDownloadThread.WorkBegin(Sender: TObject; AWorkMode: TWorkMode; const AWorkCountMax: Integer);
819 begin
820         if Assigned(OnWorkBegin) then
821                 OnWorkBegin(Sender, AWorkMode, AWorkCountMax, FNumber, FDownloadTitle);
822 end;
823
824 procedure TDownloadThread.WorkEnd(Sender: TObject; AWorkMode: TWorkMode);
825 begin
826         if Assigned(OnWorkEnd) then
827                 OnWorkEnd(Sender, AWorkMode, FNumber);
828 end;
829
830 procedure TDownloadThread.Work(Sender: TObject; AWorkMode: TWorkMode; const AWorkCount: Integer);
831 begin
832         if Assigned(OnWork) then
833                 OnWork(Sender, AWorkMode, AWorkCount, FNumber);
834 end;
835
836 function TDownloadThread.ParseCgiStatus(Content: string): TCgiStatus;
837 var
838         StatusLine: string;
839         Line: string;
840         Idx: Integer;
841         Status: string;
842         Size: string;
843         Msg: string;
844 begin
845 // [+OK] \82Ì\8fê\8d\87\82Í\8d·\95ª
846 // [-INCR] (Incorrect)\82Ì\8fê\8d\87\82Í\82·\82×\82Ä\82Ì\83f\81[\83^
847 // [-ERR (\83e\83L\83X\83g)]\82Ì\8fê\8d\87\82Í\82È\82ñ\82©\83G\83\89\81[
848 // \97á\81F+OK 23094/512K
849 //               -INCR 23094/512K
850 //               -ERR \82»\82ñ\82È\94Â\82È\82¢\82Å\82·
851         Idx := AnsiPos(#10, Content);
852         StatusLine := Copy(Content, 0, Idx);
853
854         Idx := AnsiPos(' ', Content);
855         Status := Copy(StatusLine, 0, Idx - 1);
856         Line := Copy(StatusLine, Idx + 1, Length(StatusLine));
857
858 //      Idx := AnsiPos('/', Line);
859         if Trim(Status) = '-ERR' then
860                 Msg := Trim(Line)
861         else
862                 Size := Copy(Line, 0, Idx - 1);
863
864         if Trim(Status) = '+OK' then
865                 Result.FStatus := gcsOK
866         else if Trim(Status) = '-INCR' then
867                 Result.FStatus := gcsINCR
868         else if Trim(Status) = '-ERR' then begin
869                 Result.FStatus := gcsERR;
870                 Result.FErrText := Msg;
871                 Result.FSize := 0;
872                 Exit;
873         end else begin
874                 {$IFDEF DEBUG}
875                 Writeln(Status);
876                 {$ENDIF}
877                 Result.FStatus := gcsERR;
878                 Result.FErrText := '\83G\83\89\81[\82È\82ñ\82¾\82¯\82Ç\81A\82æ\82­\95ª\82©\82ç\82È\82¢\83G\83\89\81[';
879                 Result.FSize := 0;
880                 Exit;
881         end;
882
883         if GikoSys.IsNumeric(Size) then
884                 Result.FSize := StrToInt(Size)
885         else begin
886                 Result.FSize := 0;
887                 Result.FStatus := gcsERR;
888                 Result.FErrText := '\83X\83e\81[\83^\83X\89ð\90Í\8e¸\94s[' + StatusLine + ']';
889         end;
890 end;
891
892 //\82P\8ds\96Ú\82ð\8fÁ\82µ\82Ä\81A\83R\83\93\83e\83\93\83c\83T\83C\83Y\82ð\90Ý\92è\82·\82é
893 procedure TDownloadThread.DeleteStatusLine(Item: TDownloadItem);
894 var
895         SList: TStringList;
896 begin
897         SList := TStringList.Create;
898         try
899                 SList.Text := Item.Content;
900                 //1\8ds\96Ú\82ð\8dí\8f\9c
901                 if SList.Count > 1 then
902                         SList.Delete(0);
903         Item.Content := SList.Text;
904                 //\89ü\8ds\83R\81[\83h\82ðCRLF -> LF\82Æ\8dl\82¦\82Ä\81A\8ds\90\94\95ª\82¾\82¯\83}\83C\83i\83X
905         Item.ContentLength := Length(SList.Text) - SList.Count;
906         finally
907                 SList.Free;
908         end;
909 end;
910
911 procedure TDownloadItem.SaveListFile;
912 var
913         i: Integer;
914         index: Integer;
915         NewItem: TThreadItem;
916         NumCount: Integer;
917         Body: TStringList;
918         Rec: TSubjectRec;
919         {$IFDEF DEBUG}
920         st, rt : Cardinal;
921         {$ENDIF}
922         function MakeThreadCallBack(
923                 inInstance      : DWORD;        // TBoardItem \82Ì\83C\83\93\83X\83^\83\93\83X
924                 inURL                           : PChar;        // \83X\83\8c\83b\83h\82Ì URL
925                 inTitle                 : PChar;        // \83X\83\8c\83^\83C
926                 inCount                 : DWORD         // \83\8c\83X\82Ì\90\94
927         ) : Boolean; stdcall;           // \97ñ\8b\93\82ð\91±\82¯\82é\82È\82ç True
928         var
929                 _ThreadItem     : TThreadItem;  // '_' \82Í\83N\83\89\83X\82Ì\83v\83\8d\83p\83e\83B\82Æ\82©\82Ô\82Á\82Ä\82é\82Ì\82Å
930                 boardItem               : TBoard;
931         begin
932                 Result          := True;
933                 boardItem       := TBoard( inInstance );
934
935                 boardItem.IntData := boardItem.IntData + 1;
936                 if boardItem.IntData < (boardItem.Count shr      2) then
937                         index := boardItem.GetIndexFromURL( string( inURL ) )
938                 else
939                         index := boardItem.GetIndexFromURL( string( inURL ), True );
940                 if index = -1 then begin
941                         //\90V\82µ\82¢\83X\83\8c\83b\83h
942                         _ThreadItem := TThreadItem.Create( boardItem.BoardPlugIn, boardItem, string( inURL ) );
943
944                         _ThreadItem.Title                                       := string( inTitle );
945                         _ThreadItem.AllResCount         := inCount;
946                         _ThreadItem.ParentBoard         := Board;
947                         _ThreadItem.No                                          := boardItem.IntData;
948                         _ThreadItem.RoundDate           := ZERO_DATE;
949                         _ThreadItem.LastModified        := ZERO_DATE;
950                         _ThreadItem.AgeSage                             := gasNew;
951                         boardItem.Add(_ThreadItem);
952                 end else begin
953                         //\8f\87\88Ê\82ª\8fã\82ª\82Á\82Ä\82¢\82ê\82ÎAge\82É\82·\82é
954                         if boardItem.Items[index].No > boardItem.IntData then
955                                 boardItem.Items[index].AgeSage := gasAge
956                         //\8f\87\88Ê\82ª\8fã\82ª\82Á\82Ä\82È\82¢\82¯\82Ç\81A\83\8c\83X\82ª\82Â\82¢\82Ä\82½\82ç\81ASage\82É
957                         else if boardItem.Items[index].AllResCount <> inCount then
958                                 boardItem.Items[index].AgeSage := gasSage
959                         //\8f\87\88Ê\8fã\82ª\82Á\82Ä\82È\82¢\82µ\81A\83\8c\83X\82Ì\91\9d\8c¸\82à\96³\82¯\82ê\82Î\81ANone
960                         else
961                                 boardItem.Items[index].AgeSage := gasNone;
962
963                         boardItem.Items[index].No                                               := boardItem.IntData;
964                         boardItem.Items[index].AllResCount      := inCount;
965                 end;
966
967         end;
968 begin
969 {$IFDEF DEBUG}
970         st := GetTickCount;
971         Writeln('SAVELIST');
972 {$ENDIF}
973         //Board.ListData := TList.Create;
974         Body := TStringList.Create;
975         try
976                 //\83_\83E\83\93\83\8d\81[\83h\93ú\8e\9e\90Ý\92è\81i\83\8d\81[\83J\83\8b\93ú\8e\9e\81j
977                 Board.RoundDate := Now;
978                 //\83T\81[\83o\8fã\83t\83@\83C\83\8b\82Ì\8dX\90V\8e\9e\8d\8f\90Ý\92è
979                 Board.LastModified := LastModified;
980
981
982                 //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é
983                 if GikoSys.Setting.DatOchiSortIndex >= 0 then begin
984                         Sort.SetSortNoFlag(true);
985                         Sort.SetSortOrder(GikoSys.Setting.DatOchiSortOrder);
986                         Sort.SetSortIndex(GikoSys.Setting.DatOchiSortIndex);
987                         //Sort.SortNonAcquiredCountFlag := GikoSys.Setting.NonAcquiredCount;
988                         Board.CustomSort(ThreadItemSortProc);
989                 end;
990
991 {$IFDEF DEBUG}
992         rt := GetTickCount - st;
993         Writeln('END Sortd' + IntToStr(rt) + ' ms');
994 {$ENDIF}
995
996                 for i := Board.Count - 1 downto 0 do
997                         Board.Items[i].AgeSage := gasNull;
998
999                 if Board.IsBoardPlugInAvailable then begin
1000                         // \90V\82µ\82¢\83\8a\83X\83g\82ð\8dì\90¬\82·\82é
1001                         // \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Á
1002                         // \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¬
1003                         Board.IntData := 0;
1004 {$IFDEF DEBUG}
1005         rt := GetTickCount - st;
1006         Writeln('Start Enum' + IntToStr(rt) + ' ms');
1007 {$ENDIF}
1008
1009                         //\82±\82ê\82ª\92x\82¢\81@\97v\89ü\91P
1010                         Board.BeginUpdate;
1011                         Board.BoardPlugIn.EnumThread( DWORD( Board ), @MakeThreadCallBack );
1012                         Board.EndUpdate;
1013
1014 {$IFDEF DEBUG}
1015         rt := GetTickCount - st;
1016         Writeln('End Enum' + IntToStr(rt) + ' ms');
1017 {$ENDIF}
1018
1019                         //\8cÃ\82¢\83\8a\83X\83g\82É\82µ\82©\82È\82¢\82â\82Â\82ç\82ð\8dí\8f\9c
1020                         for i := Board.Count - 1 downto 0 do begin
1021                                 if( Board.Items[i].AgeSage = gasNull )and not (Board.Items[i].IsLogFile) then
1022                                         Board.Delete(i);
1023                         end;
1024
1025                         // \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Á
1026                         for i := 0 to Board.Count - 1 do begin
1027                                 if(Board.Items[i].AgeSage = gasNull) and (Board.Items[i].IsLogFile) then begin
1028                                         Board.IntData := Board.IntData + 1;
1029                                         Board.Items[i].No                                               := Board.IntData;
1030                                         Board.Items[i].AllResCount      := Board.Items[i].Count;
1031                                         Board.Items[i].NewResCount      := 0;
1032                                         Board.Items[i].AgeSage                  := gasArch;
1033                                 end;
1034                         end;
1035
1036                 end else begin
1037                         //\90V\82µ\82¢\83\8a\83X\83g\82ð\8dì\90¬\82·\82é
1038                         //\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Á
1039                         //\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¬
1040                         Body.Text := Content;
1041                         NumCount := 0;
1042                         for i := 0 to Body.Count - 1 do begin
1043                                 Rec := GikoSys.DivideSubject(Body[i]);
1044                                 Rec.FFileName := Trim(Rec.FFileName);
1045                                 if (Rec.FTitle = '') and (Rec.FCount = 0) then Continue;
1046                                 Inc(NumCount);
1047                                 index := Board.GetIndexFromFileName(Rec.FFileName);
1048                                 if index = -1 then begin
1049                                         //\90V\82µ\82¢\83X\83\8c\83b\83h
1050                                         NewItem := TThreadItem.Create(
1051                                                                 nil,
1052                                 Board,
1053                                 GikoSys.Get2chBoard2ThreadURL( Board, ChangeFileExt( Rec.FFileName, '' ) ) );
1054                                         NewItem.Title := Rec.FTitle;
1055                                         NewItem.AllResCount := Rec.FCount;
1056                                         NewItem.ParentBoard := Board;
1057                                         NewItem.No := NumCount;
1058                                         NewItem.RoundDate := ZERO_DATE;
1059                                         NewItem.LastModified := ZERO_DATE;
1060                                         NewItem.AgeSage := gasNew;
1061                                         Board.Add(NewItem);
1062                                 end else begin
1063                                         if Board.Items[index].No > NumCount then
1064                                                 Board.Items[index].AgeSage := gasAge
1065                                         else if Board.Items[index].AllResCount < Rec.FCount then
1066                                                 Board.Items[index].AgeSage := gasSage
1067                                         else
1068                                                 Board.Items[index].AgeSage := gasNone;
1069
1070                                         Board.Items[index].No := NumCount;
1071                                         Board.Items[index].AllResCount := Rec.FCount;
1072                                 end;
1073                         end;
1074                         //\8cÃ\82¢\83\8a\83X\83g\82Ì\8dí\8f\9c
1075                         for i := Board.Count - 1 downto 0 do begin
1076                                 if( Board.Items[i].AgeSage = gasNull )and not (Board.Items[i].IsLogFile) then
1077                                         Board.Delete(i);
1078                         end;
1079
1080                         //\90V\82µ\82¢\83\8a\83X\83g\82É\96³\82©\82Á\82½\83A\83C\83e\83\80\82Ì\8dX\90V
1081                         for i := 0 to Board.Count - 1 do begin
1082                                 if( Board.Items[i].AgeSage = gasNull )and (Board.Items[i].IsLogFile) then begin
1083                                         inc(NumCount);
1084                                         Board.Items[i].No := NumCount;
1085                                         Board.Items[i].AllResCount := Board.Items[i].Count;
1086                                         Board.Items[i].NewResCount := 0;
1087                                         Board.Items[i].AgeSage := gasArch;
1088                                 end;
1089                         end;
1090                         //\83\8a\83X\83g(subject.txt)\82ð\95Û\91
1091                         GikoSys.ForceDirectoriesEx(ExtractFilePath(Board.GetSubjectFileName));
1092                         Body.SaveToFile(Board.GetSubjectFileName);
1093                 end;
1094         finally
1095                 Body.Free;
1096         end;
1097
1098
1099 end;
1100
1101 {procedure TDownloadItem.SaveListFile;
1102 var
1103         i: Integer;
1104         index: Integer;
1105         NewItem: TThreadItem;
1106         NewList: TList;
1107 //      SaveCount: Integer;
1108         NumCount: Integer;
1109         Body: TStringList;
1110         Rec: TSubjectRec;
1111 begin
1112         NewList := TList.Create;
1113         Body := TStringList.Create;
1114         try
1115                 //\8f\84\89ñ\93ú\8e\9e\90Ý\92è
1116                 Board.RoundDate := Now;
1117                 //\83T\81[\83o\8fã\83t\83@\83C\83\8b\82Ì\8dX\90V\8e\9e\8d\8f\90Ý\92è
1118                 Board.LastModified := LastModified;
1119
1120                 //\83\8a\83X\83g\95Û\91\8c\8f\90\94\8eæ\93¾
1121                 //SaveCount := MaxInt;
1122
1123                 //\8cÃ\82¢\83\8a\83X\83g\82©\82ç\83\8d\83O\96³\82µ\83A\83C\83e\83\80\82ð\8dí\8f\9c
1124                 for i := Board.Count - 1 downto 0 do
1125                         if not Board.Items[i].IsLogFile then
1126                                 Board.Delete(i);
1127
1128                 //\90V\82µ\82¢\83\8a\83X\83g\82ð\8dì\90¬\82·\82é
1129                 //\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Á
1130                 //\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¬
1131                 Body.Text := Content;
1132 //              Loop := Min(Body.Count, SaveCount);
1133                 NumCount := 0;
1134 //              for i := 0 to Loop - 1 do begin
1135                 for i := 0 to Body.Count - 1 do begin
1136                         if i = 0 then Continue; //\82P\8ds\96Ú\82Í\83X\83e\81[\83^\83X\8ds\82Ì\82½\82ß\8f\88\97\9d\82È\82µ
1137
1138                         Rec := GikoSys.DivideSubject(Body[i]);
1139                         if (Rec.FTitle = '') and (Rec.FCount = 0) then Continue;
1140                         Inc(NumCount);
1141                         index := Board.GetIndex(Rec.FFileName);
1142                         if index = -1 then begin
1143                                 NewItem := TThreadItem.Create;
1144                                 NewItem.FileName := Rec.FFileName;
1145                                 NewItem.Title := Rec.FTitle;
1146                                 NewItem.Count := Rec.FCount;
1147                                 NewItem.ParentBoard := Board;
1148                                 NewItem.No := NumCount;
1149                                 NewItem.RoundDate := ZERO_DATE;
1150                                 NewItem.LastModified := ZERO_DATE;
1151                                 NewList.Add(NewItem);
1152                         end else begin
1153                                 //Board.Items[index].Count := Count;
1154                                 Board.Items[index].No := NumCount;
1155                                 NewList.Add(Board.Items[index]);
1156                                 Board.DeleteList(index);
1157                         end;
1158                 end;
1159
1160                 //\90V\82µ\82¢\83\8a\83X\83g\82É\96³\82©\82Á\82½\8cÃ\82¢\83\8d\83O\97L\82è\83A\83C\83e\83\80\82ð\90V\82µ\82¢\83\8a\83X\83g\82É\92Ç\89Á
1161                 for i := 0 to Board.Count - 1 do begin
1162                         inc(NumCount);
1163                         Board.Items[i].No := NumCount;
1164                         NewList.Add(Board.Items[i]);
1165                 end;
1166
1167                 //\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
1168                 for i := Board.Count - 1 downto 0 do
1169                         Board.DeleteList(i);
1170
1171                 //\90V\82µ\82¢\83\8a\83X\83g\82ð\83{\81[\83h\83I\83u\83W\83F\83N\83g\82É\92Ç\89Á
1172                 for i := 0 to NewList.Count - 1 do
1173                         Board.Add(TThreadItem(NewList[i]));
1174
1175                 //\83\8a\83X\83g(subject.txt)\82ð\95Û\91
1176 //              GikoSys.ForceDirectoriesEx(GikoSys.GetLogDir + Board.BBSID);
1177 //              Body.SaveToFile(GikoSys.GetSubjectFileName(Board.BBSID));
1178                 GikoSys.ForceDirectoriesEx(ExtractFilePath(Board.GetSubjectFileName));
1179                 Body.SaveToFile(Board.GetSubjectFileName);
1180         finally
1181                 Body.Free;
1182                 NewList.Free;
1183         end;
1184 end;
1185 }
1186 procedure TDownloadItem.SaveItemFile;
1187 var
1188         Body, oldBody: TStringList;
1189         Cnt: Integer;
1190         OldCnt: Integer;
1191         FileName: string;
1192         ini: TMemIniFile;
1193         Res: TResRec;
1194         NewRes: Integer;
1195         finish : Boolean;
1196         loopCnt : Integer;
1197         LastIdx : Integer;
1198 begin
1199         FileName := ThreadItem.GetThreadFileName;
1200
1201         //if not ThreadItem.IsBoardPlugInAvailable then begin
1202     if not ThreadItem.ParentBoard.IsBoardPlugInAvailable then begin
1203                 if Trim(Content) = '' then
1204                         Exit;
1205
1206                 GikoSys.ForceDirectoriesEx(ExtractFilePath(FileName));
1207
1208                 //      Cnt := 0;
1209                 Body := TStringList.Create;
1210                 NewRes := 0;
1211                 OldCnt := 0;
1212                 try
1213                 //              if FileExists(FileName) and (ResponseCode = 206) then begin
1214                         if FileExists(FileName) and (State = gdsDiffComplete) then begin
1215                                 loopCnt := 10;
1216                                 repeat
1217                                         finish := true;
1218                                         try
1219                                                 Body.LoadFromFile(FileName);
1220                                                 OldCnt := Body.Count;
1221                                                 Body.Text := Body.Text + Content;
1222                                                 Body.SaveToFile(FileName);
1223                                                 NewRes := Body.Count - OldCnt;
1224                                         except
1225                                                 on E:EFOpenError do begin
1226                                                         sleep(10);
1227                                                         Dec(loopCnt);
1228                                                         if loopCnt > 0 then
1229                                                                 finish := false;
1230                                                 end;
1231                                         end;
1232                                 until finish;
1233                                 //Cnt := Body.Count;
1234                         end else begin
1235                                 if IsAbone then begin
1236                                         // \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µ
1237                                         oldBody := TStringList.Create;
1238                                         try
1239                                                 loopCnt := 10;
1240                                                 repeat
1241                                                         finish := true;
1242                                                         try
1243                                                                 oldBody.LoadFromFile(FileName);
1244                                                         except
1245                                                                 on E:EFOpenError do begin
1246                                                                         sleep(10);
1247                                                                         Dec(loopCnt);
1248                                                                         if loopCnt > 0 then
1249                                                                                 finish := false
1250                                                                         else
1251                                                                                 finish := true;
1252                                                                 end;
1253                                                         end;
1254                                                 until finish;
1255
1256                                                 Body.Text := Content;
1257                                                 if (ThreadItem.Kokomade > 0) and (ThreadItem.Kokomade <= oldBody.Count) then begin
1258                                                         ThreadItem.Kokomade := Body.IndexOf(oldBody.Strings[ ThreadItem.Kokomade - 1 ]);
1259                                                         if ThreadItem.Kokomade <> -1 then ThreadItem.Kokomade := ThreadItem.Kokomade + 1;
1260                                                 end;
1261
1262                                                 LastIdx := oldBody.Count;
1263                                                 repeat
1264                                                         Dec(LastIdx);
1265                                                         OldCnt := Body.IndexOf(oldBody.Strings[ LastIdx ]) + 1;
1266                                                 until ( OldCnt <> 0 ) or (LastIdx = 0);
1267
1268                                                 if OldCnt >= Body.Count then OldCnt := Body.Count - 1;
1269                                                 NewRes := Body.Count - OldCnt;
1270
1271                                                 // \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)
1272                                                 if ThreadItem.Kokomade > OldCnt then begin
1273                                                         if OldCnt > 0 then
1274                                                                 ThreadItem.Kokomade := OldCnt
1275                                                         else
1276                                                                 ThreadItem.Kokomade := 1;
1277                                                 end;
1278
1279                                         finally
1280                                                 oldBody.Free;
1281                                         end;
1282
1283                                 end else begin
1284                                         Body.Text := Content;
1285                                         //ThreadItem.Count := 0;
1286                                         OldCnt := 0;
1287                                         NewRes := Body.Count;
1288                                         //Cnt := Body.Count;
1289                                 end;
1290         //                      if Body.Count > 0 then
1291         //                              Body.Delete(0);
1292                                 Body.SaveToFile(FileName);
1293
1294                                 if ThreadItem.Title = '' then begin
1295                                         THTMLCreate.DivideStrLine(Body[0], @Res);
1296                                         ThreadItem.Title := Res.FTitle;
1297                                 end;
1298                                 ThreadItem.Size := 0;
1299                         end;
1300                         Cnt := Body.Count;
1301                 finally
1302                         Body.Free;
1303                 end;
1304
1305                 ThreadItem.Size := ThreadItem.Size + ContentLength;
1306                 ThreadItem.LastModified := LastModified;
1307                 ThreadItem.Count := Cnt;
1308                 //ThreadItem.AllResCount := Cnt;
1309                 ThreadItem.NewResCount := NewRes;
1310                 ThreadItem.NewReceive := OldCnt + 1;
1311         end;
1312         ThreadItem.AllResCount := ThreadItem.Count;
1313         ThreadItem.IsLogFile := True;
1314         ThreadItem.RoundDate := Now;
1315         if not ThreadItem.UnRead then begin
1316                 ThreadItem.UnRead := True;
1317         end;
1318 //      if ThreadItem.RoundNo = 6 then
1319 //              ThreadItem.RoundNo := 0;
1320
1321         //\88Ù\8fí\8fI\97¹\8e\9e\82Í\83C\83\93\83f\83b\83N\83X\82ª\8dX\90V\82³\82ê\82È\82¢\82½\82ß\81A\83e\83\93\83|\83\89\83\8a\82ð\8dì\90¬\82·\82é\81B
1322         //\90³\8fí\8fI\97¹\8e\9e\82É\82Í\8dí\8f\9c
1323         //\88Ù\8fí\8fI\97¹\8e\9e\82Í\81A\8e\9f\89ñ\8bN\93®\8e\9e\82É\83e\83\93\83|\83\89\83\8a\82ð\8c©\82Ä\8dX\90V
1324         ini := TMemIniFile.Create(ChangeFileExt(FileName, '.tmp'));
1325         try
1326                 ini.WriteDateTime('Setting', 'RoundDate', ThreadItem.RoundDate);
1327                 ini.WriteDateTime('Setting', 'LastModified', ThreadItem.LastModified);
1328                 ini.WriteInteger('Setting', 'Size', ThreadItem.Size);
1329                 ini.WriteInteger('Setting', 'Count', ThreadItem.Count);
1330                 ini.WriteInteger('Setting', 'AllResCount', ThreadItem.AllResCount);
1331                 ini.WriteInteger('Setting', 'NewResCount', ThreadItem.NewResCount);
1332                 ini.WriteInteger('Setting', 'NewReceive', ThreadItem.NewReceive);
1333 //              ini.WriteInteger('Setting', 'RoundNo', ThreadItem.RoundNo);
1334 //              ini.WriteBool('Setting', 'Round', ThreadItem.Round);
1335                 ini.WriteBool('Setting', 'UnRead', ThreadItem.UnRead);
1336                 ini.UpdateFile;
1337         finally
1338                 ini.Free;
1339         end;
1340 end;
1341
1342 end.