OSDN Git Service

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