OSDN Git Service

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