OSDN Git Service

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