Windows, SysUtils, Classes, ComCtrls, Controls, Forms, IdHTTP,
{HTTPApp,} YofUtils, IdGlobal, IdException, IdComponent, IniFiles, {DateUtils,}
GikoSystem, BoardGroup, ExternalBoardManager, ExternalBoardPlugInMain,
- Sort;
+ Sort, SyncObjs, bmRegExp;
type
TDownloadItem = class;
FErrText: string;
end;
-
+ TWorkData = record
+ //FWorkCS: TCriticalSection;
+ //IdHttp\82ÌOnWork\81AOnWorkBegin,OnWorkEnd\82Í\95Ê\82Ì\83X\83\8c\83b\83h\82©\82ç
+ //\8cÄ\82Î\82ê\82é\88×\81ASynchronize\82Å\93¯\8aú\82·\82é\95K\97v\82ª\82 \82é\81B
+ //\83N\83\8a\83e\83B\83J\83\8b\83Z\83N\83V\83\87\83\93\82Í\96³\82\82Ä\82à\82½\82Ô\82ñ\95½\8bC\82¾\82¯\82Ç\97p\90S\82Ì\88×\81B by eggcake
+ FWorkCS: TCriticalSection;
+ FSender: TObject;
+ FAWorkMode: TWorkMode;
+ FAWorkCount: Integer;
+ FAWorkCountMax: Integer
+ end;
TDownloadThread = class(TThread)
private
FIndy: TIdHttp;
FOnDownloadEnd: TDownloadEndEvent;
FOnDownloadMsg: TDownloadMsgEvent;
FDownloadTitle: string;
+ FWorkData: TWorkData;
procedure FireDownloadEnd;
procedure FireDownloadMsg;
procedure WorkEnd(Sender: TObject; AWorkMode: TWorkMode);
procedure Work(Sender: TObject; AWorkMode: TWorkMode; const AWorkCount: Integer);
function ParseCgiStatus(Content: string): TCgiStatus;
+ function ParseRokkaStatus(Content: string): TCgiStatus;
function CgiDownload(ItemType: TGikoDownloadType; URL: string; Modified: TDateTime): Boolean;
function DatDownload(ItemType: TGikoDownloadType; URL: string; Modified: TDateTime; RangeStart: Integer; AdjustLen: Integer): Boolean;
procedure DeleteStatusLine(Item: TDownloadItem);
procedure InitHttpClient(client: TIdHttp);
procedure ClearHttpClient(client: TIdHttp);
- protected
+ procedure FireWork;
+ procedure FireWorkBegin;
+ procedure FireWorkEnd;
+ procedure GetLastModified;
+ protected
procedure Execute; override;
public
property Item: TDownloadItem read FItem write FItem;
constructor TDownloadThread.Create(CreateSuspended: Boolean);
begin
inherited Create(CreateSuspended);
+ FWorkData.FWorkCS := TCriticalSection.Create;
+
FIndy := TIdHttp.Create(nil);
FIndy.OnWorkBegin := WorkBegin;
begin
ClearHttpClient(FIndy);
FIndy.Free;
+ FWorkData.FWorkCS.Free;
inherited;
end;
client.RecvBufferSize := Gikosys.Setting.RecvBufferSize;
client.ProxyParams.BasicAuthentication := False;
client.ReadTimeout := GikoSys.Setting.ReadTimeOut;
+ client.ConnectTimeout := GikoSys.Setting.ReadTimeOut;
{$IFDEF DEBUG}
Writeln('------------------------------------------------------------');
{$ENDIF}
lastContent : string;
logFile : TFileStream;
adjustMargin : Integer;
+ Host: String;
+ Sep: Integer;
const
ADJUST_MARGIN = 16;
begin
end;
}
+ if (Item.DownType = gdtThread) and (Item.ResponseCode = 302) then begin
+ {$IFDEF DEBUG}
+ Writeln('offlaw2.so\82Å\8eæ\93¾');
+ {$ENDIF}
+ ATitle := Item.ThreadItem.Title;
+ if ATitle = '' then
+ ATitle := '\81i\96¼\8fÌ\95s\96¾\81j';
+ FMsg := '\81\9adat.gz\82ª\91¶\8dÝ\82µ\82È\82¢\82½\82ßofflaw2.so\82ð\97\98\97p\82µ\82Ü\82· - [' + ATitle + ']';
+ FIcon := gmiWhat;
+ if Assigned(OnDownloadMsg) then
+ Synchronize(FireDownloadMsg);
+ URL := Item.ThreadItem.GetOfflaw2SoURL;
+ Modified := Item.ThreadItem.LastModified;
+ RangeStart := 0;
+ AdjustLen := 0;
+ if not DatDownload(Item.DownType, URL, Modified, RangeStart, AdjustLen) then begin
+ {$IFDEF DEBUG}
+ Writeln('ResponseCode: ' + IntToStr(Item.ResponseCode));
+ {$ENDIF}
+ Item.State := gdsError;
+
+ if (Item.DownType = gdtThread) and (Item.ResponseCode = 302) then begin
+ 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';
+ FIcon := gmiNG;
+ if Assigned(OnDownloadMsg) then
+ Synchronize(FireDownloadMsg);
+ end;
+
+ end else begin
+ {$IFDEF DEBUG}
+ Writeln('ResponseCode: ' + IntToStr(Item.ResponseCode));
+ {$ENDIF}
+ if Item.ResponseCode = 200 then begin
+ {$IFDEF DEBUG}
+ Writeln('CGIStatus: OK');
+ {$ENDIF}
+ if Copy(Item.Content, 1, 5) = 'ERROR' then begin
+ {$IFDEF DEBUG}
+ Writeln('Offlow2Error');
+ {$ENDIF}
+ Item.ResponseCode := 404;
+ Item.State := gdsError;
+ Item.ErrText := '\83X\83\8c\82Í\91¶\8dÝ\82µ\82È\82¢\82æ\82¤\82Å\82·\81B' + Item.Content;
+ end else begin
+ GetLastModified;
+ end;
+ end else begin
+ {$IFDEF DEBUG}
+ Writeln('CGIStatus: 404(ERROR)');
+ {$ENDIF}
+ Item.ResponseCode := 404;
+ Item.State := gdsError;
+ Item.ErrText := CgiStatus.FErrText;
+ end;
+ end;
+ end;
+
+ if (Item.DownType = gdtThread) and ((Item.ResponseCode = 302) or (Item.ResponseCode = 404)) then begin
+ FSessionID := '';
+ Synchronize(GetSessionID);
+ if (FSessionID <> '') then begin
+ {$IFDEF DEBUG}
+ Writeln('Rokka\82Å\8eæ\93¾');
+ {$ENDIF}
+ ATitle := Item.ThreadItem.Title;
+ if ATitle = '' then
+ ATitle := '\81i\96¼\8fÌ\95s\96¾\81j';
+ FMsg := '\81\9aofflow2.so\82É\91¶\8dÝ\82µ\82È\82¢\82½\82ßRokka\82ð\97\98\97p\82µ\82Ü\82· - [' + ATitle + ']';
+ FIcon := gmiWhat;
+ if Assigned(OnDownloadMsg) then
+ Synchronize(FireDownloadMsg);
+ URL := Item.ThreadItem.GetRokkaURL(FSessionID);
+ Modified := Item.ThreadItem.LastModified;
+ RangeStart := 0;
+ AdjustLen := 0;
+
+ if not DatDownload(Item.DownType, URL, Modified, RangeStart, AdjustLen) then begin
+ {$IFDEF DEBUG}
+ Writeln('ResponseCode: ' + IntToStr(Item.ResponseCode));
+ {$ENDIF}
+ Item.State := gdsError;
+ end else begin
+ CgiStatus := ParseRokkaStatus(Item.Content);
+ {$IFDEF DEBUG}
+ Writeln('ResponseCode: ' + IntToStr(Item.ResponseCode));
+ {$ENDIF}
+ case CgiStatus.FStatus of
+ gcsOK: begin
+ {$IFDEF DEBUG}
+ Writeln('CGIStatus: OK');
+ {$ENDIF}
+ Item.ResponseCode := 200;
+ DeleteStatusLine(Item);
+ end;
+ gcsERR: begin
+ {$IFDEF DEBUG}
+ Writeln('CGIStatus: 404(ERROR)');
+ {$ENDIF}
+ Item.ResponseCode := 404;
+ Item.State := gdsError;
+ Item.ErrText := CgiStatus.FErrText;
+ end;
+ end;
+ end;
+ end;
+ end;
+
+ //********************
+ // 2ch\8aO\95\94\94Â
+ //********************
+// if not GikoSys.Is2chHost(GikoSys.UrlToServer(URL)) then begin
+ Host := URL;
+ Sep := Pos('://', Host);
+ if (Sep > 0) then
+ Delete(Host, 1, Sep + 2);
+ Sep := Pos('/', Host);
+ if (Sep > 0) then
+ SetLength(Host, Sep - 1);
+ if not GikoSys.Is2chHost(Host) then begin
+ if (Item.DownType = gdtThread) and (Item.ResponseCode = 404) then begin
+ {$IFDEF DEBUG}
+ Writeln('\8aO\95\94\94Â\89ß\8b\8e\83\8d\83O\8eæ\93¾');
+ {$ENDIF}
+ ATitle := Item.ThreadItem.Title;
+ if ATitle = '' then
+ ATitle := '\81i\96¼\8fÌ\95s\96¾\81j';
+ FMsg := '\81\9a\82Q\82¿\82á\82ñ\82Ë\82é\8aO\95\94\94Â\82Ì\89ß\8b\8e\83\8d\83O\8eæ\93¾\82ð\8ds\82¢\82Ü\82· - [' + ATitle + ']';
+ FIcon := gmiWhat;
+ if Assigned(OnDownloadMsg) then
+ Synchronize(FireDownloadMsg);
+ URL := Item.ThreadItem.GetExternalBoardKakoDatURL;
+ Modified := Item.ThreadItem.LastModified;
+ RangeStart := 0;
+ AdjustLen := 0;
+ if not DatDownload(Item.DownType, URL, Modified, RangeStart, AdjustLen) then
+ Item.State := gdsError;
+ {$IFDEF DEBUG}
+ Writeln('ResponseCode: ' + IntToStr(Item.ResponseCode));
+ {$ENDIF}
+ end;
+ end;
+(*
//********************
//offlaw.cgi\82Å\8eæ\93¾
//********************
Synchronize(FireDownloadMsg);
end;
end;
-
+*)
case Item.ResponseCode of
200: Item.State := gdsComplete;
206: Item.State := gdsDiffComplete;
end;
end;
-procedure TDownloadThread.WorkBegin(Sender: TObject; AWorkMode: TWorkMode; const AWorkCountMax: Integer);
+procedure TDownloadThread.WorkBegin(Sender: TObject;
+AWorkMode: TWorkMode; const AWorkCountMax: Integer);
begin
- if Assigned(OnWorkBegin) then
- OnWorkBegin(Sender, AWorkMode, AWorkCountMax, FNumber, FDownloadTitle);
+ if Assigned(OnWorkBegin) then begin
+ FWorkData.FWorkCS.Acquire;
+ try
+ FWorkData.FSender := Sender;
+ FWorkData.FAWorkMode := AWorkMode;
+ FWorkData.FAWorkCountMax := AWorkCountMax;
+ Synchronize(FireWorkBegin);
+ finally
+ FWorkData.FWorkCS.Release;
+ end;
+ end;
end;
-procedure TDownloadThread.WorkEnd(Sender: TObject; AWorkMode: TWorkMode);
+procedure TDownloadThread.WorkEnd(Sender: TObject;
+AWorkMode: TWorkMode);
begin
- if Assigned(OnWorkEnd) then
- OnWorkEnd(Sender, AWorkMode, FNumber);
+ if Assigned(OnWorkEnd) then begin;
+ FWorkData.FWorkCS.Acquire;
+ try
+ FWorkData.FSender := Sender;
+ FWorkData.FAWorkMode := AWorkMode;
+ Synchronize(FireWorkEnd);
+ finally
+ FWorkData.FWorkCS.Release;
+ end;
+ end;
+end;
+
+procedure TDownloadThread.Work(Sender: TObject; AWorkMode:
+TWorkMode; const AWorkCount: Integer);
+begin
+ if Assigned(OnWork) then begin
+ FWorkData.FWorkCS.Acquire;
+ try
+ FWorkData.FSender := Sender;
+ FWorkData.FAWorkMode := AWorkMode;
+ FWorkData.FAWorkCount := AWorkCount;
+ Synchronize(FireWork);
+ finally
+ FWorkData.FWorkCS.Release;
+ end;
+ end;
end;
-procedure TDownloadThread.Work(Sender: TObject; AWorkMode: TWorkMode; const AWorkCount: Integer);
+//\82±\82±\82©\82ç\90V\8bK\83\81\83\\83b\83h
+procedure TDownloadThread.FireWorkBegin;
begin
- if Assigned(OnWork) then
- OnWork(Sender, AWorkMode, AWorkCount, FNumber);
+ OnWorkBegin(FWorkData.FSender, FWorkData.FAWorkMode,
+ FWorkData.FAWorkCountMax,
+ FNumber, FDownloadTitle);
+end;
+
+procedure TDownloadThread.FireWorkEnd;
+begin
+ OnWorkEnd(FWorkData.FSender, FWorkData.FAWorkMode,
+ FNumber);
+end;
+
+procedure TDownloadThread.FireWork;
+begin
+ OnWork(FWorkData.FSender, FWorkData.FAWorkMode,
+ FWorkData.FAWorkCount,
+ FNumber);
end;
function TDownloadThread.ParseCgiStatus(Content: string): TCgiStatus;
end;
end;
+function TDownloadThread.ParseRokkaStatus(Content: string): TCgiStatus;
+var
+ StatusLine: string;
+ Idx: Integer;
+ Status: string;
+begin
+// \81@\83\8c\83X\83|\83\93\83X : 1\8ds\96Ú\82Érokka\82Ì\8f\88\97\9d\8c\8b\89Ê\82ª\8bL\8fq\82³\82ê\82Ü\82·
+// \81@\81@"Success XXX"\81@- \90¬\8c÷\81@XXX\82Édat\82Ì\8fó\91Ô\81i\8eæ\93¾\8c³\81j\82ª\8bL\8fq\82³\82ê\82Ü\82·
+// \81@\81@\81@\81@\81@\81@\81@\81@\81@\81@\81@Live\81@\81@\81@\81@\83\89\83C\83u\83X\83\8c\83b\83h
+// \81@\81@\81@\81@\81@\81@\81@\81@\81@\81@\81@Pool\81@\81@\81@\81@dat\97\8e\82¿\83X\83\8c\83b\83h
+// \81@\81@\81@\81@\81@\81@\81@\81@\81@\81@\81@Archive \81@\81@\89ß\8b\8e\83\8d\83O
+// \81@\81@\81@\81@\81@\81@\81@\81@\81@\81@ \88È\8d~\82Ì\8ds\82ÉDAT\8c`\8e®(name<>email<>datetime<>body<>[title])\82Å\83\8d\83O\82ª\8bL\8fq\82³\82ê\82Ä\82¢\82Ü\82·
+// \81@\81@"Error XXX"\81@\81@- \89½\82ç\82©\82Ì\83G\83\89\81[\82Å\82·\81@XXX \82ª\83G\83\89\81[\83R\81[\83h\82Å\82·\81B
+// \81@\81@\81@\81@\81@\81@\81@\81@\81@\81@\81@13 \81@\81@\81@not found\81@\81@\81@\81@\81@\81@\97v\8b\81\82³\82ê\82½dat\82ª\8c©\82Â\82©\82è\82Ü\82¹\82ñ\82Å\82µ\82½
+// \81@\81@\81@\81@\81@\81@\81@\81@\81@\81@\81@8008135\81@inputError \81@\81@\81@\81@\81@\83\8a\83N\83G\83X\83gURL\82ÌSERVER\82©BOARD\82ª\90³\82µ\82\82È\82¢\82Å\82·
+// \81@\81@\81@\81@\81@\81@\81@\81@\81@\81@\81@666\81@\81@\81@urlError \81@\81@\81@\81@\81@\81@OPTIONS\82Ü\82½\82ÍQueryString\82ª\90³\82µ\82\82È\82¢\82Å\82·
+// \81@\81@\81@\81@\81@\81@\81@\81@\81@\81@\81@69 \81@\81@\81@authenticationError\81@KAGI\82ª\95s\90³\81i\97L\8cø\8aú\8cÀ\90Ø\82ê\82»\82Ì\91¼\81j
+// \81@\81@\81@\81@\81@\81@\81@\81@\81@\81@\81@420\81@\81@\81@timeLimitError \81@\81@\81@\83A\83N\83Z\83X\8aÔ\8au\82ª\92Z\82·\82¬\82Ü\82·
+// \81@\81@\81@\81@\81@\81@\81@\81@\81@\81@\81@42 \81@\81@\81@methodError\81@\81@\81@\81@\81@\82»\82ÌHTTP\83\81\83\\83b\83h\82Í\8b\96\89Â\82³\82ê\82Ä\82¢\82Ü\82¹\82ñ
+ Idx := AnsiPos(#10, Content);
+ if (Idx > 0) then
+ StatusLine := Copy(Content, 0, Idx)
+ else
+ StatusLine := Content;
+
+ if (AnsiPos('Success', StatusLine) = 1) then begin
+ Result.FStatus := gcsOK;
+ Delete(StatusLine, 1, 7);
+ Status := Trim(StatusLine);
+ if (Status = 'Live') then // \91½\95ª\82±\82ê\82Í\97\88\82È\82¢
+ Result.FErrText := '\8eæ\93¾\90¬\8c÷\81i\83\89\83C\83u\83X\83\8c\83b\83h\81j'
+ else if (Status = 'Pool') then
+ Result.FErrText := '\8eæ\93¾\90¬\8c÷\81idat\97\8e\82¿\83X\83\8c\83b\83h\81j'
+ else if (Status = 'Archive') then
+ Result.FErrText := '\8eæ\93¾\90¬\8c÷\81i\89ß\8b\8e\83\8d\83O\81j'
+ else // ???
+ Result.FErrText := '\8eæ\93¾\90¬\8c÷';
+ end
+ else if (AnsiPos('Error', StatusLine) = 1) then begin
+ Result.FStatus := gcsERR;
+ Delete(StatusLine, 1, 5);
+ Status := Trim(StatusLine);
+ if (Status = '13') then
+ Result.FErrText := '\97v\8b\81\82³\82ê\82½dat\82ª\8c©\82Â\82©\82è\82Ü\82¹\82ñ\82Å\82µ\82½'
+ else if (Status = '8008135') then
+ Result.FErrText := '\83\8a\83N\83G\83X\83gURL\82ÌSERVER\82©BOARD\82ª\90³\82µ\82\82È\82¢\82Å\82·'
+ else if (Status = '666') then
+ Result.FErrText := 'OPTIONS\82Ü\82½\82ÍQueryString\82ª\90³\82µ\82\82È\82¢\82Å\82·'
+ else if (Status = '69') then
+ Result.FErrText := 'KAGI\82ª\95s\90³\81i\97L\8cø\8aú\8cÀ\90Ø\82ê\82»\82Ì\91¼\81j'
+ else if (Status = '20') then
+ Result.FErrText := '\83A\83N\83Z\83X\8aÔ\8au\82ª\92Z\82·\82¬\82Ü\82·'
+ else if (Status = '42') then
+ Result.FErrText := '\82»\82ÌHTTP\83\81\83\\83b\83h\82Í\8b\96\89Â\82³\82ê\82Ä\82¢\82Ü\82¹\82ñ'
+ else // ???
+ Result.FErrText := '\8eæ\93¾\83G\83\89\81[[' + Status + ']';
+ end
+ else begin
+ Result.FStatus := gcsERR;
+ Result.FErrText := '\83X\83e\81[\83^\83X\89ð\90Í\8e¸\94s[' + StatusLine + ']';
+ end;
+ Result.FSize := 0;
+end;
+
//\82P\8ds\96Ú\82ð\8fÁ\82µ\82Ä\81A\83R\83\93\83e\83\93\83c\83T\83C\83Y\82ð\90Ý\92è\82·\82é
procedure TDownloadThread.DeleteStatusLine(Item: TDownloadItem);
var
end;
end;
+procedure TDownloadThread.GetLastModified;
+var
+ ResultDate: TDateTime;
+ ResList: TStringList;
+ LastRes: String;
+ KwPos: Integer;
+ ResRow: Integer;
+ DTIdx: Integer;
+ Ok: Boolean;
+ AWKStr: TAWKStr;
+ RStart: Integer;
+ RLength: Integer;
+begin
+ AWKStr := TAWKStr.Create(nil);
+ Ok := False;
+ ResultDate := Item.LastModified;
+ ResList := TStringList.Create;
+ try
+ ResList.Text := Item.Content;
+ for ResRow := ResList.Count - 1 downto 0 do begin
+ if (ResRow > 999) then
+ continue;
+ LastRes := ResList.Strings[ResRow];
+ KwPos := Pos('<>', LastRes);
+ if (KwPos < 1) then
+ continue;
+ Delete(LastRes, 1, KwPos + 1);
+ KwPos := Pos('<>', LastRes);
+ if (KwPos < 1) then
+ continue;
+ Delete(LastRes, 1, KwPos + 1);
+ // '2013/04/22(\8c\8e) 02:32:36'
+ SetLength(LastRes, 23);
+ Delete(LastRes, 11, 4); // \97j\93ú\8dí\8f\9c
+
+ // \93ú\95t\8am\94F
+// AWKStr.RegExp := '(\d{4})/(0[1-9]|1[0-2])/(0[1-9]|[12][0-9]|3[01])';
+ AWKStr.RegExp := '(19|20)([0-9][0-9])/(0[1-9]|1[0-2])/(0[1-9]|[12][0-9]|3[01])';
+ if (AWKStr.Match(AWKStr.ProcessEscSeq(LastRes), RStart, RLength) = 1) then begin
+
+ AWKStr.RegExp := '([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])';
+ if (AWKStr.Match(AWKStr.ProcessEscSeq(LastRes), RStart, RLength) < 1) then begin
+ SetLength(LastRes, 10);
+ LastRes := LastRes + ' 00:00:00';
+ end;
+
+ try
+ ResultDate := StrToDateTime(LastRes);
+ Ok := True;
+ except
+ end;
+ end;
+ if (Ok = True) then
+ break;
+ end;
+ finally
+ ResList.Free;
+ end;
+ Item.LastModified :=ResultDate;
+ FreeAndNil(AWKStr);
+end;
+
end.