OSDN Git Service

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