OSDN Git Service

・レスエディタ/スレ立てエディタをUnicode文字(ShiftJISに存在しない文字)入力対応
[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, bmRegExp;
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         procedure GetLastModified;
75         protected
76                 procedure Execute; override;
77         public
78                 property Item: TDownloadItem read FItem write FItem;
79                 property Number: Integer read FNumber write FNumber;
80                 constructor Create(CreateSuspended: Boolean);
81                                 destructor Destroy; override;
82                 procedure Abort;
83                 property OnWork: TGikoWorkEvent read FOnWork write FOnWork;
84                 property OnWorkBegin: TGikoWorkBeginEvent read FOnWorkBegin write FOnWorkBegin;
85                 property OnWorkEnd: TGikoWorkEndEvent read FOnWorkEnd write FOnWorkEnd;
86                 property OnDownloadEnd: TDownloadEndEvent read FOnDownloadEnd write FOnDownloadEnd;
87                 property OnDownloadMsg: TDownloadMsgEvent read FOnDownloadMsg write FOnDownloadMsg;
88         end;
89
90         TDownloadItem = class(TObject)
91         private
92                 FDownType: TGikoDownloadType;
93                 FBoard: TBoard;
94                 FThreadItem: TThreadItem;
95
96                 FContentLength: Integer;
97                 FLastModified: TDateTime;
98                 FContent: string;
99                 FResponseCode: Smallint;
100                 FState: TGikoDownloadState;
101                 FErrText: string;
102                 FForceDownload: Boolean;
103                 FIsAbone : Boolean;
104         public
105                 procedure SaveListFile;
106                 procedure SaveItemFile;
107
108                 property DownType: TGikoDownloadType read FDownType write FDownType;
109                 property Board: TBoard read FBoard write FBoard;
110                 property ThreadItem: TThreadItem read FThreadItem write FThreadItem;
111
112                 property ContentLength: Integer read FContentLength write FContentLength;
113                 property LastModified: TDateTime read FLastModified write FLastModified;
114                 property Content: string read FContent write FContent;
115                 property ResponseCode: Smallint read FResponseCode write FResponseCode;
116                 property State: TGikoDownloadState read FState write FState;
117                 property ErrText: string read FErrText write FErrText;
118                 property ForceDownload: Boolean read FForceDownload write FForceDownload;
119                 property IsAbone : Boolean read FIsAbone write FIsAbone;
120         end;
121
122 implementation
123
124 uses
125         Y_TextConverter, MojuUtils, HTMLCreate, ReplaceDataModule;
126
127 constructor TDownloadThread.Create(CreateSuspended: Boolean);
128 begin
129         inherited Create(CreateSuspended);
130     FWorkData.FWorkCS := TCriticalSection.Create;
131
132         FIndy := TIdHttp.Create(nil);
133
134         FIndy.OnWorkBegin := WorkBegin;
135         FIndy.OnWorkEnd := WorkEnd;
136         FIndy.OnWork := Work;
137 end;
138
139 destructor TDownloadThread.Destroy;
140 begin
141         ClearHttpClient(FIndy);
142         FIndy.Free;
143     FWorkData.FWorkCS.Free;
144         inherited;
145 end;
146
147 function RFC1123_Date(aDate : TDateTime) : String;
148 const
149          StrWeekDay : String = 'MonTueWedThuFriSatSun';
150          StrMonth        : String = 'JanFebMarAprMayJunJulAugSepOctNovDec';
151 var
152          Year, Month, Day                        : Word;
153          Hour, Min,      Sec, MSec : Word;
154          DayOfWeek                                                      : Word;
155 begin
156          DecodeDate(aDate, Year, Month, Day);
157          DecodeTime(aDate, Hour, Min,    Sec, MSec);
158          DayOfWeek := ((Trunc(aDate) - 2) mod 7);
159          Result := Copy(StrWeekDay, 1 + DayOfWeek * 3, 3) + ', ' +
160                                                  Format('%2.2d %s %4.4d %2.2d:%2.2d:%2.2d',
161                                                                                 [Day, Copy(StrMonth, 1 + 3 * (Month - 1), 3),
162                                                                                  Year, Hour, Min, Sec]);
163 end;
164 // ******************************************************************
165 // HTTPClient\82Ì\8f\89\8aú\89»
166 // ******************************************************************
167 procedure TDownloadThread.InitHttpClient(client: TIdHttp);
168 begin
169         ClearHttpClient(client);
170         client.Disconnect;
171         client.Request.UserAgent := GikoSys.GetUserAgent;
172         client.RecvBufferSize := Gikosys.Setting.RecvBufferSize;
173         client.ProxyParams.BasicAuthentication := False;
174         client.ReadTimeout := GikoSys.Setting.ReadTimeOut;
175     client.ConnectTimeout := GikoSys.Setting.ReadTimeOut;
176         {$IFDEF DEBUG}
177         Writeln('------------------------------------------------------------');
178         {$ENDIF}
179         //FIndy.AllowCookies := False;
180         if GikoSys.Setting.ReadProxy then begin
181                 if GikoSys.Setting.ProxyProtocol then
182                         client.ProtocolVersion := pv1_1
183                 else
184                         client.ProtocolVersion := pv1_0;
185                 client.ProxyParams.ProxyServer := GikoSys.Setting.ReadProxyAddress;
186                 client.ProxyParams.ProxyPort := GikoSys.Setting.ReadProxyPort;
187                 client.ProxyParams.ProxyUsername := GikoSys.Setting.ReadProxyUserID;
188                 client.ProxyParams.ProxyPassword := GikoSys.Setting.ReadProxyPassword;
189                 if GikoSys.Setting.ReadProxyUserID <> '' then
190                         client.ProxyParams.BasicAuthentication := True;
191                 {$IFDEF DEBUG}
192                 Writeln('\83v\83\8d\83L\83V\90Ý\92è\82 \82è');
193                 Writeln('\83z\83X\83g: ' + GikoSys.Setting.ReadProxyAddress);
194                 Writeln('\83|\81[\83g: ' + IntToStr(GikoSys.Setting.ReadProxyPort));
195                 {$ENDIF}
196         end else begin
197                 if GikoSys.Setting.Protocol then
198                         client.ProtocolVersion := pv1_1
199                 else
200                         client.ProtocolVersion := pv1_0;
201                 client.ProxyParams.ProxyServer := '';
202                 client.ProxyParams.ProxyPort := 80;
203                 client.ProxyParams.ProxyUsername := '';
204                 client.ProxyParams.ProxyPassword := '';
205                 {$IFDEF DEBUG}
206                 Writeln('\83v\83\8d\83L\83V\90Ý\92è\82È\82µ');
207                 {$ENDIF}
208         end;
209 end;
210 // ******************************************************************
211 // HTTPClient\82Ì\83\8a\83N\83G\83X\83g\82Æ\83\8c\83X\83|\83\93\83X\82Ì\83f\81[\83^\82Ì\8fÁ\8b\8e
212 // ******************************************************************
213 procedure TDownloadThread.ClearHttpClient(client: TIdHttp);
214 begin
215         client.Request.CustomHeaders.Clear;
216         client.Request.RawHeaders.Clear;
217         client.Request.Clear;
218         client.Response.CustomHeaders.Clear;
219         client.Response.RawHeaders.Clear;
220         client.Response.Clear;
221
222         client.ProxyParams.Clear;
223 end;
224 procedure TDownloadThread.Execute;
225 var
226         ResStream: TMemoryStream;
227
228         URL: string;
229         CgiStatus: TCgiStatus;
230         Modified: TDateTime;
231         RangeStart: Integer;
232         AdjustLen: Integer;
233         Idx: Integer;
234         ATitle: string;
235         DownloadResult: Boolean;
236         boardPlugIn     : TBoardPlugIn;
237         lastContent             : string;
238         logFile                         : TFileStream;
239         adjustMargin    : Integer;
240 const
241         ADJUST_MARGIN   = 16;
242 begin
243         while not Terminated do begin
244                 //===== \83v\83\89\83O\83C\83\93
245                 FAbort := False;
246                 boardPlugIn := nil;
247                 ExternalBoardManager.OnWork                             := Work;
248                 ExternalBoardManager.OnWorkBegin        := WorkBegin;
249                 ExternalBoardManager.OnWorkEnd          := WorkEnd;
250
251                 FDownloadTitle := '';
252                 case FItem.FDownType of
253                 gdtBoard:
254                         begin
255                                 FDownloadTitle := FItem.FBoard.Title;
256                                 if FItem.FBoard <> nil then begin
257                                         if FItem.FBoard.IsBoardPlugInAvailable then begin
258                                                 boardPlugIn     := FItem.FBoard.BoardPlugIn;
259                                                 Item.State      := TGikoDownloadState( boardPlugIn.DownloadBoard( DWORD( FItem.FBoard ) ) );
260                                         end;
261                                 end;
262                         end;
263                 gdtThread:
264                         begin
265                                 FDownloadTitle := FItem.FThreadItem.Title;
266                                 if FItem.FThreadItem <> nil then begin
267                                         if FItem.FThreadItem.ParentBoard.IsBoardPlugInAvailable then begin
268                                                 boardPlugIn := FItem.FThreadItem.ParentBoard.BoardPlugIn;
269                                                 Item.State      := TGikoDownloadState( boardPlugIn.DownloadThread( DWORD( FItem.FThreadItem ) ) );
270                                         end;
271                                         //if FItem.FThreadItem.IsBoardPlugInAvailable then begin
272                                         //      boardPlugIn     := FItem.FThreadItem.BoardPlugIn;
273                                         //      Item.State      := TGikoDownloadState( boardPlugIn.DownloadThread( DWORD( FItem.FThreadItem ) ) );
274                                         //end;
275                                 end;
276                         end;
277                 end;
278                 if Length(FDownloadTitle) = 0 then
279                         FDownloadTitle := '\81i\96¼\8fÌ\95s\96¾\81j';
280
281                 if boardPlugIn <> nil then begin
282                         if FAbort then begin
283                                 Item.State := gdsAbort;
284                         end;
285                         if Assigned( OnDownloadEnd ) then
286                                 Synchronize( FireDownloadEnd );
287                         if Terminated then
288                                 Break;
289
290                         Suspend;
291                         Continue;
292                 end;
293
294                 FAbort := False;
295                 //===== \83v\83\89\83O\83C\83\93\82ð\8eg\97p\82µ\82È\82¢\8fê\8d\87
296                 InitHttpClient(FIndy);
297                 adjustMargin := 0;
298                 if Item.DownType = gdtThread then begin
299                         if FileExists( Item.ThreadItem.GetThreadFileName ) then begin
300                                 // dat \83t\83@\83C\83\8b\82Ì\8dÅ\8cã\82ð\93Ç\82Ý\8fo\82·
301                                 SetLength( lastContent, ADJUST_MARGIN + 1 );
302                                 logFile := TFileStream.Create( Item.ThreadItem.GetThreadFileName, fmOpenRead or fmShareDenyWrite );
303                                 try
304                                         logFile.Seek( -(ADJUST_MARGIN + 1), soFromEnd );
305                                         logFile.Read( lastContent[ 1 ], ADJUST_MARGIN + 1 );
306                                         lastContent := StringReplace( lastContent, #13, '', [] );       // CR \82Ì\8dí\8f\9c
307                                 finally
308                                         logFile.Free;
309                                 end;
310                         end else begin
311         lastContent := '';
312                         end;
313                         adjustMargin := Length( lastContent );
314                 end;
315
316                 FIndy.Request.ContentRangeStart := 0;
317                 FIndy.Request.LastModified := ZERO_DATE;
318                 ResStream := TMemoryStream.Create;
319                 try
320                         try
321                                 //********************
322                                 //DAT or Subject\8eæ\93¾
323                                 //********************
324                                 Item.ResponseCode := 0;
325                                 RangeStart := 0;
326                                 AdjustLen := 0;
327                                 Modified := 0;
328                                 if Item.DownType = gdtBoard then begin
329                                         {$IFDEF DEBUG}
330                                         Writeln('Subject\8eæ\93¾');
331                                         Writeln('URL: ' + Item.Board.GetReadCgiURL);
332                                         Writeln('Modified: ' + FloatToStr(Item.Board.LastModified));
333                                         {$ENDIF}
334                                         URL := Item.Board.GetReadCgiURL;
335                                         if Item.ForceDownload then begin
336                                                 // \8b­\90§\8eæ\93¾
337                                                 ATitle := Item.Board.Title;
338                                                 if ATitle = '' then
339                                                         ATitle := '\81i\96¼\8fÌ\95s\96¾\81j';
340                                                 FMsg := '\81\9a\8b­\90§\8eæ\93¾\82ð\8ds\82¢\82Ü\82· - [' + ATitle + ']';
341                                                 FIcon := gmiWhat;
342                                                 if Assigned(OnDownloadMsg) then
343                                                         Synchronize(FireDownloadMsg);
344                                                 Modified := ZERO_DATE
345                                         end else begin
346                                                 Modified := Item.Board.LastModified;
347                                         end;
348                                 end else if Item.DownType = gdtThread then begin
349                                         {$IFDEF DEBUG}
350                                         Writeln('DAT\8eæ\93¾');
351                                         Writeln('URL: ' + Item.ThreadItem.GetDatURL);
352                                         Writeln('Modified: ' + FloatToStr(Item.ThreadItem.LastModified));
353                                         {$ENDIF}
354                                         URL := Item.ThreadItem.GetDatURL;
355                                         if Item.ForceDownload then begin
356                                                 // \8b­\90§\8eæ\93¾
357                                                 ATitle := Item.ThreadItem.Title;
358                                                 if ATitle = '' then
359                                                         ATitle := '\81i\96¼\8fÌ\95s\96¾\81j';
360                                                 FMsg := '\81\9a\8b­\90§\8eæ\93¾\82ð\8ds\82¢\82Ü\82· - [' + ATitle + ']';
361                                                 FIcon := gmiWhat;
362                                                 if FileExists(ChangeFileExt(Item.FThreadItem.GetThreadFileName,'.NG')) = true then
363                                                         DeleteFile(ChangeFileExt(Item.FThreadItem.GetThreadFileName,'.NG'));
364                                                 if Assigned(OnDownloadMsg) then
365                                                         Synchronize(FireDownloadMsg);
366                                                 Modified := ZERO_DATE;
367                                                 RangeStart := 0;
368                                                 AdjustLen := 0;
369                                         end else begin
370                                                 Modified := Item.ThreadItem.LastModified;
371                                                 if Item.ThreadItem.Size > 0 then begin
372                                                         {$IFDEF DEBUG}
373                                                         Writeln('RangeStart: ' + IntToStr(Item.ThreadItem.Size));
374                                                         {$ENDIF}
375                                                         // \82 \82Ú\81[\82ñ\83`\83F\83b\83N\82Ì\82½\82ß adjustMargin \83o\83C\83g\91O\82©\82ç\8eæ\93¾
376                                                         RangeStart := Item.ThreadItem.Size;
377                                                         AdjustLen := -adjustMargin;
378                                                 end;
379                                         end;
380                                 end;
381                                 Item.IsAbone := False;
382                                 DownloadResult := DatDownload(Item.DownType, URL, Modified, RangeStart, AdjustLen);
383                                 {$IFDEF DEBUG}
384                                 Writeln('ResponseCode: ' + IntToStr(FIndy.ResponseCode));
385                                 {$ENDIF}
386                                 if Item.DownType = gdtThread then begin
387                                         if Item.ResponseCode = 416 then begin
388                                                 Item.IsAbone := True;
389                                                 DownloadResult := True;
390                                         end else if DownloadResult and (AdjustLen < 0) then begin
391                                                 if Copy( Item.Content, 1, adjustMargin ) <> lastContent then
392                                                         Item.IsAbone := True;
393                                         end;
394                                 end;
395
396                                 if Trim(FIndy.Response.RawHeaders.Values['Date']) <> '' then begin
397                                         if Item.DownType = gdtBoard then
398                                                 Item.Board.LastGetTime := GikoSys.DateStrToDateTime(FIndy.Response.RawHeaders.Values['Date'])
399                                         else
400                                                 Item.ThreadItem.ParentBoard.LastGetTime := GikoSys.DateStrToDateTime(FIndy.Response.RawHeaders.Values['Date']);
401                                 end;
402
403                                 if DownloadResult then begin
404                                         {$IFDEF DEBUG}
405                                         Writeln('Date:' + FIndy.Response.RawHeaders.Values['Date']);
406                                         {$ENDIF}
407                                         if Item.IsAbone then begin
408                                                 {$IFDEF DEBUG}
409                                                 Writeln('\82 \82Ú\81[\82ñ\8c\9f\8fo');
410                                                 {$ENDIF}
411                                                 ATitle := Item.ThreadItem.Title;
412                                                 if ATitle = '' then
413                                                         ATitle := '\81i\96¼\8fÌ\95s\96¾\81j';
414                                                 //\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¾
415                                                 RangeStart := 0;
416                                                 AdjustLen := 0;
417                                                 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 + ']';
418                                                 FIcon := gmiWhat;
419                                                 if FileExists(ChangeFileExt(Item.FThreadItem.GetThreadFileName,'.NG')) = true then
420                                                         DeleteFile(ChangeFileExt(Item.FThreadItem.GetThreadFileName,'.NG'));
421                                                 if Assigned(OnDownloadMsg) then
422                                                         Synchronize(FireDownloadMsg);
423                                                 if not DatDownload(Item.DownType, URL, ZERO_DATE, RangeStart, AdjustLen) then
424                                                         Item.State := gdsError;
425                                                 {$IFDEF DEBUG}
426                                                 Writeln('\82 \82Ú\81[\82ñ\8dÄ\8eæ\93¾\8cã');
427                                                 Writeln('ResponseCode: ' + IntToStr(Item.ResponseCode));
428                                                 {$ENDIF}
429                                         end else if (Item.DownType = gdtThread) and (AdjustLen < 0) then begin
430                                                 // \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
431                                                 Item.Content := Copy(Item.Content, adjustMargin + 1, MaxInt);
432                                         end;
433                                 end else begin
434                                         Item.State := gdsError;
435                                         if (Item.DownType = gdtBoard) and (Item.ResponseCode = 302) then begin
436                                                 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';
437                                                 FIcon := gmiNG;
438                                                 if Assigned(OnDownloadMsg) then
439                                                         Synchronize(FireDownloadMsg);
440                                         end;
441                                 end;
442
443                                 //********************
444                                 //dat.gz\8eæ\93¾(1)
445                                 //********************
446                                 if (Item.DownType = gdtThread) and (Item.ResponseCode = 302) then begin
447                                         {$IFDEF DEBUG}
448                                         Writeln('dat.gz\8eæ\93¾');
449                                         {$ENDIF}
450                                         ATitle := Item.ThreadItem.Title;
451                                         if ATitle = '' then
452                                                 ATitle := '\81i\96¼\8fÌ\95s\96¾\81j';
453                                         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 + ']';
454                                         FIcon := gmiWhat;
455                                         if Assigned(OnDownloadMsg) then
456                                                 Synchronize(FireDownloadMsg);
457                                         URL := Item.ThreadItem.GetDatgzURL;
458                                         Modified := Item.ThreadItem.LastModified;
459                                         RangeStart := 0;
460                                         AdjustLen := 0;
461                                         if not DatDownload(Item.DownType, URL, Modified, RangeStart, AdjustLen) then
462                                                 Item.State := gdsError;
463                                         {$IFDEF DEBUG}
464                                         Writeln('ResponseCode: ' + IntToStr(Item.ResponseCode));
465                                         {$ENDIF}
466                                 end;
467
468                                 //********************
469                                 //dat.gz\81@\81¨\81@dat\82Ì\8eæ\93¾\81@2005\94N6\8c\8e\92Ç\89Á\81@by\82à\82\82ã
470                                 //********************
471                                 if (Item.DownType = gdtThread) and (Item.ResponseCode = 302) then begin
472                                         {$IFDEF DEBUG}
473                                         Writeln('dat\8eæ\93¾');
474                                         {$ENDIF}
475                                         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 + ']';
476                                         FIcon := gmiWhat;
477                                         if Assigned(OnDownloadMsg) then
478                                                 Synchronize(FireDownloadMsg);
479                                         URL := ChangeFileExt(URL, '');
480                                         Modified := Item.ThreadItem.LastModified;
481                                         RangeStart := 0;
482                                         AdjustLen := 0;
483                                         if not DatDownload(Item.DownType, URL, Modified, RangeStart, AdjustLen) then
484                                                 Item.State := gdsError;
485                                         {$IFDEF DEBUG}
486                                         Writeln('ResponseCode: ' + IntToStr(Item.ResponseCode));
487                                         {$ENDIF}
488                                 end;
489
490                                 //********************
491                                 //dat.gz\8eæ\93¾(2)
492                                 //********************
493 {
494                                 if (Item.DownType = gdtThread) and (Item.ResponseCode = 302) then begin
495                                         ATitle := Item.ThreadItem.Title;
496                                         if ATitle = '' then
497                                                 ATitle := '\81i\96¼\8fÌ\95s\96¾\81j';
498                                         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 + ']';
499                                         FIcon := gmiWhat;
500                                         if Assigned(OnDownloadMsg) then
501                                                 Synchronize(FireDownloadMsg);
502                                         URL := Item.ThreadItem.GetOldDatgzURL;
503                                         Modified := Item.ThreadItem.LastModified;
504                                         RangeStart := 0;
505                                         AdjustLen := 0;
506                                         if not DatDownload(Item.DownType, URL, Modified, RangeStart, AdjustLen) then
507                                                 Item.State := gdsError;
508                                 end;
509 }
510
511                                 if (Item.DownType = gdtThread) and (Item.ResponseCode = 302) then begin
512                                         {$IFDEF DEBUG}
513                                         Writeln('offlaw2.so\82Å\8eæ\93¾');
514                                         {$ENDIF}
515                                         ATitle := Item.ThreadItem.Title;
516                                         if ATitle = '' then
517                                                 ATitle := '\81i\96¼\8fÌ\95s\96¾\81j';
518                                         FMsg := '\81\9adat.gz\82ª\91\8dÝ\82µ\82È\82¢\82½\82ßofflaw2.so\82ð\97\98\97p\82µ\82Ü\82· - [' + ATitle + ']';
519                                         FIcon := gmiWhat;
520                                         if Assigned(OnDownloadMsg) then
521                                                 Synchronize(FireDownloadMsg);
522                                         URL := Item.ThreadItem.GetOfflaw2SoURL;
523                                         Modified := Item.ThreadItem.LastModified;
524                                         RangeStart := 0;
525                                         AdjustLen := 0;
526                                         if not DatDownload(Item.DownType, URL, Modified, RangeStart, AdjustLen) then begin
527                                                 {$IFDEF DEBUG}
528                                                 Writeln('ResponseCode: ' + IntToStr(Item.ResponseCode));
529                                                 {$ENDIF}
530                                                 Item.State := gdsError;
531
532                                                 if (Item.DownType = gdtThread) and (Item.ResponseCode = 302) then begin
533                                                         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';
534                                                         FIcon := gmiNG;
535                                                         if Assigned(OnDownloadMsg) then
536                                                                 Synchronize(FireDownloadMsg);
537                                                 end;
538
539                                         end else begin
540                                                 {$IFDEF DEBUG}
541                                                 Writeln('ResponseCode: ' + IntToStr(Item.ResponseCode));
542                                                 {$ENDIF}
543                                                 if Item.ResponseCode = 200 then begin
544                                                         {$IFDEF DEBUG}
545                                                         Writeln('CGIStatus: OK');
546                                                         {$ENDIF}
547                             if Copy(Item.Content, 1, 5) = 'ERROR' then begin
548                                 {$IFDEF DEBUG}
549                                 Writeln('Offlow2Error');
550                                 {$ENDIF}
551                                 Item.ResponseCode := 404;
552                                 Item.State := gdsError;
553                                 Item.ErrText := '\83X\83\8c\82Í\91\8dÝ\82µ\82È\82¢\82æ\82¤\82Å\82·\81B' + Item.Content;
554                             end else begin
555                                 GetLastModified;
556                             end;
557                                                 end else begin
558                                                         {$IFDEF DEBUG}
559                                                         Writeln('CGIStatus: 404(ERROR)');
560                                                         {$ENDIF}
561                                                         Item.ResponseCode := 404;
562                                                         Item.State := gdsError;
563                                                         Item.ErrText := CgiStatus.FErrText;
564                                                 end;
565                                         end;
566                                 end;
567
568                                 if (Item.DownType = gdtThread) and ((Item.ResponseCode = 302) or (Item.ResponseCode = 404)) then begin
569                                 FSessionID := '';
570                                 Synchronize(GetSessionID);
571                     if (FSessionID <> '') then begin
572                         {$IFDEF DEBUG}
573                         Writeln('Rokka\82Å\8eæ\93¾');
574                         {$ENDIF}
575                         ATitle := Item.ThreadItem.Title;
576                         if ATitle = '' then
577                             ATitle := '\81i\96¼\8fÌ\95s\96¾\81j';
578                         FMsg := '\81\9aofflow2.so\82É\91\8dÝ\82µ\82È\82¢\82½\82ßRokka\82ð\97\98\97p\82µ\82Ü\82· - [' + ATitle + ']';
579                         FIcon := gmiWhat;
580                         if Assigned(OnDownloadMsg) then
581                             Synchronize(FireDownloadMsg);
582                         URL := Item.ThreadItem.GetRokkaURL(FSessionID);
583                         Modified := Item.ThreadItem.LastModified;
584                         RangeStart := 0;
585                         AdjustLen := 0;
586
587                         if not DatDownload(Item.DownType, URL, Modified, RangeStart, AdjustLen) then begin
588                             {$IFDEF DEBUG}
589                             Writeln('ResponseCode: ' + IntToStr(Item.ResponseCode));
590                             {$ENDIF}
591                             Item.State := gdsError;
592                         end else begin
593                             CgiStatus := ParseRokkaStatus(Item.Content);
594                             {$IFDEF DEBUG}
595                             Writeln('ResponseCode: ' + IntToStr(Item.ResponseCode));
596                             {$ENDIF}
597                             case CgiStatus.FStatus of
598                                 gcsOK: begin
599                                     {$IFDEF DEBUG}
600                                     Writeln('CGIStatus: OK');
601                                     {$ENDIF}
602                                     Item.ResponseCode := 200;
603                                     DeleteStatusLine(Item);
604                                 end;
605                                 gcsERR: begin
606                                     {$IFDEF DEBUG}
607                                     Writeln('CGIStatus: 404(ERROR)');
608                                     {$ENDIF}
609                                     Item.ResponseCode := 404;
610                                     Item.State := gdsError;
611                                     Item.ErrText := CgiStatus.FErrText;
612                                 end;
613                             end;
614                         end;
615                     end;
616                                 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 procedure TDownloadThread.GetLastModified;
1604 var
1605         ResultDate: TDateTime;
1606         ResList: TStringList;
1607         LastRes: String;
1608         KwPos: Integer;
1609     ResRow: Integer;
1610     DTIdx: Integer;
1611     Ok: Boolean;
1612     AWKStr: TAWKStr;
1613         RStart: Integer;
1614         RLength: Integer;
1615 begin
1616         AWKStr := TAWKStr.Create(nil);
1617     Ok := False;
1618         ResultDate := Item.LastModified;
1619         ResList := TStringList.Create;
1620         try
1621                 ResList.Text := Item.Content;
1622         for ResRow := ResList.Count - 1 downto 0 do begin
1623             if (ResRow > 999) then
1624                 continue;
1625                         LastRes := ResList.Strings[ResRow];
1626                         KwPos := Pos('<>', LastRes);
1627                         if (KwPos < 1) then
1628                 continue;
1629                         Delete(LastRes, 1, KwPos + 1);
1630                         KwPos := Pos('<>', LastRes);
1631                         if (KwPos < 1) then
1632                 continue;
1633                         Delete(LastRes, 1, KwPos + 1);
1634                         // '2013/04/22(\8c\8e) 02:32:36'
1635                         SetLength(LastRes, 23);
1636                         Delete(LastRes, 11, 4);         // \97j\93ú\8dí\8f\9c
1637
1638             // \93ú\95t\8am\94F
1639 //            AWKStr.RegExp := '(\d{4})/(0[1-9]|1[0-2])/(0[1-9]|[12][0-9]|3[01])';
1640             AWKStr.RegExp := '(19|20)([0-9][0-9])/(0[1-9]|1[0-2])/(0[1-9]|[12][0-9]|3[01])';
1641                 if (AWKStr.Match(AWKStr.ProcessEscSeq(LastRes), RStart, RLength) = 1) then begin
1642
1643                 AWKStr.RegExp := '([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])';
1644                 if (AWKStr.Match(AWKStr.ProcessEscSeq(LastRes), RStart, RLength) < 1) then begin
1645                                 SetLength(LastRes, 10);
1646                     LastRes := LastRes + ' 00:00:00';
1647                 end;
1648
1649                 try
1650                     ResultDate := StrToDateTime(LastRes);
1651                     Ok := True;
1652                 except
1653                 end;
1654             end;
1655             if (Ok = True) then
1656                 break;
1657                 end;
1658         finally
1659                 ResList.Free;
1660         end;
1661     Item.LastModified :=ResultDate;
1662         FreeAndNil(AWKStr);
1663 end;
1664
1665 end.