OSDN Git Service

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