OSDN Git Service

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