OSDN Git Service

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