OSDN Git Service

・TBoardItem に対応。
authoryoffy <yoffy>
Fri, 14 Nov 2003 18:54:20 +0000 (18:54 +0000)
committeryoffy <yoffy>
Fri, 14 Nov 2003 18:54:20 +0000 (18:54 +0000)
res/ExternalBoardPlugIn/ShitarabaPlugIn.dpr

index 18e38cf..1c525d3 100644 (file)
@@ -16,36 +16,126 @@ uses
        YofUtils in '..\..\YofUtils.pas',
        PlugInMain in 'PlugInMain.pas',
        ThreadItem in 'ThreadItem.pas',
+       BoardItem in 'BoardItem.pas',
        FilePath in 'FilePath.pas',
        PzConv in 'PzConv.pas';
 
 {$R *.res}
 
 type
+       // =========================================================================
+       // TShitarabaThreadItem
+       // =========================================================================
        TShitarabaThreadItem = class(TThreadItem)
        private
                FIsTemporary    : Boolean;
                FDat                                    : TStringList;
 
        public
-               constructor Create( inInstance : DWORD );
-               destructor Destroy; override;
+               constructor     Create( inInstance : DWORD );
+               destructor      Destroy; override;
 
        private
-               procedure Download;
-               function GetRes( inNo : Integer ) : string;
-               function GetHeader( inOptionalHeader : string ) : string;
-               function GetFooter( inOptionalFooter : string ) : string;
+               function        Download : TDownloadState;
+               function        GetRes( inNo : Integer ) : string;
+               function        GetHeader( inOptionalHeader : string ) : string;
+               function        GetFooter( inOptionalFooter : string ) : string;
 
                procedure       To2chDat( ioHTML : TStringList );
        end;
 
+       // =========================================================================
+       // TShitarabaBoardItem
+       // =========================================================================
+       TShitarabaBoardItem = class(TBoardItem)
+       private
+               FIsTemporary    : Boolean;
+               FDat                                    : TStringList;
+
+       public
+               constructor     Create( inInstance : DWORD );
+               destructor      Destroy; override;
+
+       private
+               function        Download : TDownloadState;
+               procedure       EnumThread( inCallBack : TBoardItemEnumThreadCallBack );
+
+       end;
+
 const
-       PLUGIN_NAME                             = 'ExternalShitarabaReader';
+       LOG_DIR                                         = 'shitaraba\';
+
+       PLUGIN_NAME                             = 'ShitarabaPlugIn';
        MAJOR_VERSION                   = 1;
        MINOR_VERSION                   = 0;
        RELEASE_VERSION         = 'develop';
-       REVISION_VERSION        = 1;
+       REVISION_VERSION        = 2;
+
+// =========================================================================
+// \8eG\97p\8aÖ\90\94
+// =========================================================================
+
+// *************************************************************************
+// \83e\83\93\83|\83\89\83\8a\82È\83p\83X\82Ì\8eæ\93¾
+// *************************************************************************
+function TemporaryFile : string;
+var
+       tempPath : array [0..MAX_PATH] of       char;
+begin
+
+       GetTempPath( SizeOf(tempPath), tempPath );
+       repeat
+               Result := tempPath + IntToStr( Random( $7fffffff ) );
+       until not FileExists( Result );
+
+end;
+
+// *************************************************************************
+// \82µ\82½\82ç\82Î\97p\83\8d\83O\83t\83H\83\8b\83_\8eæ\93¾
+// *************************************************************************
+function MyLogFolder : string;
+var
+       folder : string;
+begin
+
+       folder := LogFolder;
+       if Length( folder ) = 0 then
+               Result := ''
+       else
+               Result := folder + LOG_DIR;
+
+end;
+
+(*************************************************************************
+ *\83f\83B\83\8c\83N\83g\83\8a\82ª\91\8dÝ\82·\82é\82©\83`\83F\83b\83N
+ *************************************************************************)
+function DirectoryExistsEx(const Name: string): Boolean;
+var
+       Code: Integer;
+begin
+       Code := GetFileAttributes(PChar(Name));
+       Result := (Code <> -1) and (FILE_ATTRIBUTE_DIRECTORY and Code <> 0);
+end;
+
+(*************************************************************************
+ *\83f\83B\83\8c\83N\83g\83\8a\8dì\90¬\81i\95¡\90\94\8aK\91w\91Î\89\9e\81j
+ *************************************************************************)
+function ForceDirectoriesEx(Dir: string): Boolean;
+begin
+       Result := True;
+       if Length(Dir) = 0 then
+               raise Exception.Create('\83t\83H\83\8b\83_\82ª\8dì\90¬\8fo\97\88\82Ü\82¹\82ñ');
+       Dir := ExcludeTrailingPathDelimiter(Dir);
+       if (Length(Dir) < 3) or DirectoryExistsEx(Dir)
+               or (ExtractFilePath(Dir) = Dir) then Exit; // avoid 'xyz:\' problem.
+       Result := ForceDirectoriesEx(ExtractFilePath(Dir)) and CreateDir(Dir);
+end;
+
+
+
+// =========================================================================
+// PlugIn
+// =========================================================================
 
 // *************************************************************************
 // \83v\83\89\83O\83C\83\93\82Ì\83o\81[\83W\83\87\83\93\82ð\97v\8b\81\82³\82ê\82½
@@ -59,14 +149,22 @@ procedure OnVersionInfo(
 ); stdcall;
 begin
 
-       {$IFDEF DEBUG}
-       Writeln('OnVersionInfo');
-       {$ENDIF}
-       outAgent                := CreateResultString( PChar( PLUGIN_NAME ) );
-       outMajor                := MAJOR_VERSION;
-       outMinor                := MINOR_VERSION;
-       outRelease      := CreateResultString( PChar( RELEASE_VERSION ) );
-       outRevision     := REVISION_VERSION;
+       try
+               {$IFDEF DEBUG}
+               Writeln('OnVersionInfo');
+               {$ENDIF}
+               outAgent                := CreateResultString( PChar( PLUGIN_NAME ) );
+               outMajor                := MAJOR_VERSION;
+               outMinor                := MINOR_VERSION;
+               outRelease      := CreateResultString( PChar( RELEASE_VERSION ) );
+               outRevision     := REVISION_VERSION;
+       except
+               outAgent                := nil;
+               outMajor                := 0;
+               outMinor                := 0;
+               outRelease      := nil;
+               outRevision     := 0;
+       end;
 
 end;
 
@@ -83,29 +181,39 @@ const
        BBS_HOST = 'shitaraba.com';
 begin
 
-       {$IFDEF DEBUG}
-       Writeln('OnAcceptURL');
-       {$ENDIF}
-       // \97á\82Æ\82µ\82Ä\83z\83X\83g\96¼\82ª shitaraba.com \82Å\8fI\82í\82é\8fê\8d\87\82Í\8eó\82¯\95t\82¯\82é\82æ\82¤\82É\82µ\82Ä\82¢\82é
-       URI := TIdURI.Create( inURL );
        try
-               foundPos := Pos( BBS_HOST, URI.Host );
-               Result := (foundPos > 0) and (Length( URI.Host ) - foundPos + 1 = Length( BBS_HOST ))
-       finally
-               URI.Free;
+               {$IFDEF DEBUG}
+               Writeln('OnAcceptURL');
+               {$ENDIF}
+               // \97á\82Æ\82µ\82Ä\83z\83X\83g\96¼\82ª shitaraba.com \82Å\8fI\82í\82é\8fê\8d\87\82Í\8eó\82¯\95t\82¯\82é\82æ\82¤\82É\82µ\82Ä\82¢\82é
+               URI := TIdURI.Create( inURL );
+               try
+                       foundPos := Pos( BBS_HOST, URI.Host );
+                       Result := (foundPos > 0) and (Length( URI.Host ) - foundPos + 1 = Length( BBS_HOST ))
+               finally
+                       URI.Free;
+               end;
+       except
+               Result := False;
        end;
 
 end;
 
 // *************************************************************************
 // \8ew\92è\82µ\82½\83X\83\8c\88ê\97\97\82Ì\83_\83E\83\93\83\8d\81[\83h\82ð\97v\8b\81\82³\82ê\82½
-// \96ß\82è\92l\82Í\82»\82Ì\82Ü\82Ü Subject.txt \82É\95Û\91\82³\82ê\82é
 // *************************************************************************
 function OnDownloadList(
-       inURL : PChar                                           // \83_\83E\83\93\83\8d\81[\83h\82·\82é\83X\83\8c\88ê\97\97\82ð\8e¦\82µ\82½ URL
-) : PChar; stdcall;                            // \83X\83\8c\83b\83h\96¼\82Ì\88ê\97\97\82ð\95\\82µ\82½\89ü\8ds\8bæ\90Ø\82è\83e\83L\83X\83g
+       inURL                                   : PChar;                        // \83_\83E\83\93\83\8d\81[\83h\82·\82é\83X\83\8c\88ê\97\97\82ð\8e¦\82µ\82½ URL
+       var outDownResult : PChar               // \83t\83@\83C\83\8b\96¼,\83^\83C\83g\83\8b \82Ì\88ê\97\97\82ð\95\\82µ\82½\89ü\8ds\8bæ\90Ø\82è\83e\83L\83X\83g
+) : TDownloadState; stdcall;   // \83_\83E\83\93\83\8d\81[\83h\82ª\90¬\8c÷\82µ\82½\82©\82Ç\82¤\82©
 var
-       modified        : Double;
+       modified                        : Double;
+       conv                                    : TPzConv;
+       downResult              : PChar;
+       subject                         : TStringList;
+       uri                                             : TIdURI;
+       uriList                         : TStringList;
+       logPath                         : string;
 begin
 
        {$IFDEF DEBUG}
@@ -113,10 +221,132 @@ begin
        {$ENDIF}
        // \93Æ\8e©\82É\83_\83E\83\93\83\8d\81[\83h\82â\83t\83B\83\8b\83^\83\8a\83\93\83O\82ð\8ds\82í\82È\82¢\8fê\8d\87\82Í
        // InternalDownload \82É\94C\82¹\82é\82±\82Æ\82ª\8fo\97\88\82é
-       InternalDownload( inURL, modified, Result );
+       if InternalDownload( inURL, modified, downResult ) = 200 then begin
+               conv := TPzConv.Create( nil );
+               try try
+                       subject := TStringList.Create;
+
+                       uri := TIdURI.Create( string( inURL ) );
+                       uriList := TStringList.Create;
+                       try
+                               // EUC \82ð Shift_JIS \82É
+                               subject.Text    := conv.EucToSjis( string( downResult ) );
+                               // \83p\83X\82ð\8eZ\8fo
+                               YofUtils.ExtractHttpFields( ['/', '?'], [], uri.Path, uriList );
+                               // http://jbbs.shitaraba.com/game/1000/subject.txt
+                               logPath := MyLogFolder + uriList[ 1 ] + '\' + uriList[ 2 ] + '\' + uri.Document;
+                               // \95Û\91
+                               subject.SaveToFile( logPath );
+                               outDownResult := CreateResultString( subject.Text );
+                       finally
+                               uri.Free;
+                               uriList.Free;
+                               subject.Free;
+                       end;
+               finally
+                       DisposeResultString( downResult );
+                       conv.Free;
+               end; except end;
+               Result := dsComplete;
+       end else begin
+               Result := dsError;
+       end;
+
+end;
+
+// *************************************************************************
+// \8ew\92è\82µ\82½\83X\83\8c\88ê\97\97\82ð\97v\8b\81\82³\82ê\82½
+// *************************************************************************
+function OnReadList(
+       inURL                                   : PChar // \93Ç\82Ý\8d\9e\82Þ\83X\83\8c\88ê\97\97\82ð\8e¦\82µ\82½ URL
+) : PChar; stdcall;                    // \83t\83@\83C\83\8b\96¼,\83^\83C\83g\83\8b \82Ì\88ê\97\97\82ð\95\\82µ\82½\89ü\8ds\8bæ\90Ø\82è\83e\83L\83X\83g
+var
+       subject                         : TStringList;
+       uri                                             : TIdURI;
+       uriList                         : TStringList;
+       logPath                         : string;
+begin
+
+       {$IFDEF DEBUG}
+       Writeln('OnReadList');
+       {$ENDIF}
+       try
+               try
+                       subject := TStringList.Create;
+
+                       uri := TIdURI.Create( string( inURL ) );
+                       uriList := TStringList.Create;
+                       try
+                               // \83p\83X\82ð\8eZ\8fo
+                               YofUtils.ExtractHttpFields( ['/', '?'], [], uri.Path, uriList );
+                               // http://jbbs.shitaraba.com/game/1000/subject.txt
+                               logPath := MyLogFolder + uriList[ 1 ] + '\' + uriList[ 2 ] + '\' + uri.Document;
+                               if FileExists( logPath ) then begin
+                                       // \93Ç\82Ý\8d\9e\82Ý
+                                       subject.LoadFromFile( logPath );
+                                       Result := CreateResultString( subject.Text );
+                               end else begin
+                                       Result := '';
+                               end;
+                       finally
+                               uri.Free;
+                               uriList.Free;
+                               subject.Free;
+                       end;
+               finally
+               end;
+       except
+               Result := nil;
+       end;
+
+end;
+
+// *************************************************************************
+// \83X\83\8c\88ê\97\97\82Ì URL \82©\82ç\83X\83\8c\83b\83h\82Ì URL \82ð\93±\82«\8fo\82·
+// *************************************************************************
+function OnListURL2ThreadURL(
+       inListURL               : PChar;        // \83X\83\8c\88ê\97\97\82ð\8e¦\82µ\82½ URL
+       inFileName      : PChar         // \83X\83\8c\83b\83h\83t\83@\83C\83\8b\96¼
+) : PChar; stdcall;                    // \83X\83\8c\83b\83h\82Ì URL
+var
+       listURL                 : string;
+       fileName                : string;
+       threadURL               : string;
+       uri                                     : TIdURI;
+       uriList                 : TStringList;
+begin
+
+       listURL         := string( inListURL );
+       fileName        := string( inFileName );
+       fileName        := Copy( fileName, 1, Max( 0, Pos( fileName, '.' ) - 1 ) );
+       if Copy( listURL, Length( listURL ), 1 ) = '/' then
+               uri := TIdURI.Create( listURL )
+       else
+               uri := TIdURI.Create( listURL + '/' );
+       uriList := TStringList.Create;
+
+       try
+               try
+                       YofUtils.ExtractHttpFields( ['/', '?'], [], uri.Path, uriList );
+                       threadURL       := uri.Protocol + '://' + uri.Host + '/bbs/read.cgi/' +
+                               uriList[ 1 ] + '/' + uriList[ 2 ] + '/' + fileName + '/l100';
+                       Result          := CreateResultString( threadURL );
+               finally
+                       uri.Free;
+                       uriList.Free;
+               end;
+       except
+               Result := nil;
+       end;
 
 end;
 
+
+
+// =========================================================================
+// TShitarabaThreadItem
+// =========================================================================
+
 // *************************************************************************
 // \83R\83\93\83X\83g\83\89\83N\83^
 // *************************************************************************
@@ -137,8 +367,7 @@ begin
 
        FilePath                        := '';
        FIsTemporary    := False;
-
-       Randomize;
+       FDat                                    := nil;
 
        if Copy( URL, Length( URL ), 1 ) = '/' then
                uri := TIdURI.Create( URL )
@@ -147,7 +376,7 @@ begin
        uriList := TStringList.Create;
        try
                YofUtils.ExtractHttpFields( ['/', '?'], [], uri.Path, uriList );
-               FilePath        := LogFolder + 'shitaraba\' + uriList.Strings[ 3 ] + '\' + uriList.Strings[ 4 ] + '\' + uriList.Strings[ 5 ] + '.dat';
+               FilePath        := MyLogFolder + uriList[ 3 ] + '\' + uriList[ 4 ] + '\' + uriList[ 5 ] + '.dat';
                IsLogFile       := FileExists( FilePath );
        finally
                uri.Free;
@@ -179,49 +408,9 @@ begin
 end;
 
 // *************************************************************************
-// \83e\83\93\83|\83\89\83\8a\82È\83p\83X\82Ì\8eæ\93¾(\8eG\97p\8aÖ\90\94)
-// *************************************************************************
-function TemporaryFile : string;
-var
-       tempPath : array [0..MAX_PATH] of       char;
-begin
-
-       GetTempPath( SizeOf(tempPath), tempPath );
-       repeat
-               Result := tempPath + IntToStr( Random( $7fffffff ) );
-       until not FileExists( Result );
-
-end;
-
-(*************************************************************************
- *\83f\83B\83\8c\83N\83g\83\8a\82ª\91\8dÝ\82·\82é\82©\83`\83F\83b\83N
- *************************************************************************)
-function DirectoryExistsEx(const Name: string): Boolean;
-var
-       Code: Integer;
-begin
-       Code := GetFileAttributes(PChar(Name));
-       Result := (Code <> -1) and (FILE_ATTRIBUTE_DIRECTORY and Code <> 0);
-end;
-
-(*************************************************************************
- *\83f\83B\83\8c\83N\83g\83\8a\8dì\90¬\81i\95¡\90\94\8aK\91w\91Î\89\9e\81j
- *************************************************************************)
-function ForceDirectoriesEx(Dir: string): Boolean;
-begin
-       Result := True;
-       if Length(Dir) = 0 then
-               raise Exception.Create('\83t\83H\83\8b\83_\82ª\8dì\90¬\8fo\97\88\82Ü\82¹\82ñ');
-       Dir := ExcludeTrailingPathDelimiter(Dir);
-       if (Length(Dir) < 3) or DirectoryExistsEx(Dir)
-               or (ExtractFilePath(Dir) = Dir) then Exit; // avoid 'xyz:\' problem.
-       Result := ForceDirectoriesEx(ExtractFilePath(Dir)) and CreateDir(Dir);
-end;
-
-// *************************************************************************
 // \8ew\92è\82µ\82½ URL \82Ì\83X\83\8c\83b\83h\82Ì\83_\83E\83\93\83\8d\81[\83h\82ð\8ew\8e¦\82³\82ê\82½
 // *************************************************************************
-procedure TShitarabaThreadItem.Download;
+function TShitarabaThreadItem.Download : TDownloadState;
 var
        modified                        : Double;
        tmp                                             : PChar;
@@ -237,6 +426,8 @@ begin
        {$IFDEF DEBUG}
        Writeln('Download');
        {$ENDIF}
+       Result := dsError;
+
        if Copy( URL, Length( URL ), 1 ) = '/' then
                uri := TIdURI.Create( URL )
        else
@@ -246,14 +437,16 @@ begin
                YofUtils.ExtractHttpFields( ['/', '?'], [], uri.Path, uriList );
                // http://jbbs.shitaraba.com/bbs/read.cgi/game/1578/1067968274/l100
                // protocol://host/1/2/3/4/5/uriList.Count - 1
-               if LogFolder = '' then begin
+               if MyLogFolder = '' then begin
                        // \82Ç\82±\82É\95Û\91\82µ\82Ä\82¢\82¢\82Ì\82©\95ª\82©\82ç\82È\82¢\82Ì\82Å\88ê\8e\9e\83t\83@\83C\83\8b\82É\95Û\91
                        FilePath                        := TemporaryFile;
                        FIsTemporary    := True;
                end else begin
-                       FilePath        := LogFolder + 'shitaraba\' + uriList.Strings[ 3 ] + '\' + uriList.Strings[ 4 ] + '\' + uriList.Strings[ 5 ] + '.dat';
+                       FilePath                        := MyLogFolder + uriList[ 3 ] + '\' + uriList[ 4 ] + '\' + uriList[ 5 ] + '.dat';
+                       FIsTemporary    := False;
                end;
 
+               // \95Û\91\97p\82Ì\83f\83B\83\8c\83N\83g\83\8a\82ð\8c@\82é
                ForceDirectoriesEx( Copy( FilePath, 1, LastDelimiter( '\', FilePath ) ) );
 
                // \81«----- \8dÅ\8fI\93I\82É\8dí\8f\9c\82·\82é\82±\82Æ -----\81«
@@ -268,7 +461,7 @@ begin
                                except
                                end;
                        end;
-                       FDat                    := TStringList.Create;
+                       FDat := TStringList.Create;
                        try
                                // dat \82Ì\93Ç\82Ý\8d\9e\82Ý
                                FDat.LoadFromFile( FilePath );
@@ -291,8 +484,8 @@ begin
                        modified        := LastModified;
                        datURL          :=
                                uri.Protocol + '://' + uri.Host + '/bbs/read.cgi/' +
-                               uriList.Strings[ 3 ] + '/' + uriList.Strings[ 4 ] + '/' +
-                               uriList.Strings[ 5 ] + '/' + IntToStr( AllResCount + 1 ) + '-n'; // \90V\92\85\82Ì\82Ý
+                               uriList[ 3 ] + '/' + uriList[ 4 ] + '/' +
+                               uriList[ 5 ] + '/' + IntToStr( AllResCount + 1 ) + '-n'; // \90V\92\85\82Ì\82Ý
                        responseCode := InternalDownload( PChar( datURL ), modified, tmp, 0 );
 
                        try
@@ -321,14 +514,16 @@ begin
                                                                logStream.Position      := logStream.Size;
                                                                logStream.Write( PChar( downResult.Text )^, Length( downResult.Text ) );
 
+                                                               if AllResCount = 0 then
+                                                                       Result := dsComplete
+                                                               else
+                                                                       Result := dsDiffComplete;
                                                                LastModified                            := modified;
                                                                IsLogFile                                               := True;
                                                                NewReceive                                      := AllResCount + 1;
                                                                AllResCount                                     := AllResCount + downResult.Count;
                                                                NewResCount                                     := downResult.Count;
                                                        end;
-                                               end else begin
-                                                       responseCode := -1;
                                                end;
                                        finally
                                                downResult.Free;
@@ -366,25 +561,25 @@ begin
        {$ENDIF}
        // \93Æ\8e©\82É\83t\83B\83\8b\83^\83\8a\83\93\83O\82ð\8ds\82í\82È\82¢\8fê\8d\87\82Í
        // InternalAbon \82¨\82æ\82Ñ Dat2HTML \82É\94C\82¹\82é\82±\82Æ\82ª\8fo\97\88\82é
+       if FDat = nil then begin
+               if IsLogFile then begin
+                       // dat \82Ì\93Ç\82Ý\8d\9e\82Ý
+                       FDat                    := TStringList.Create;
+                       FDat.LoadFromFile( FilePath );
+
+                       AllResCount := FDat.Count; // \96{\93\96\82Í\82±\82±\82Å\82â\82é\82×\82«\82Å\82Í\82È\82¢\82ª\8eÀ\8c±\82Ì\82½\82ß\82Ì\88ê\8e\9e\93I\82È\82à\82Ì
+               end else begin
+                       // \83\8d\83O\82É\91\8dÝ\82µ\82È\82¢\82Ì\82Å\82±\82Ì\82Ü\82Ü\8fI\97¹
+                       Result := '';
+                       Exit;
+               end;
+       end;
+       res                     := FDat[ inNo - 1 ];
+       tmp                     := InternalAbon( PChar( res ) );
        try
-       if FDat = nil then
-      if IsLogFile then begin
-        // dat \82Ì\93Ç\82Ý\8d\9e\82Ý
-        FDat                   := TStringList.Create;
-        FDat.LoadFromFile( FilePath );
-
-        AllResCount := FDat.Count; // \96{\93\96\82Í\82±\82±\82Å\82â\82é\82×\82«\82Å\82Í\82È\82¢\82ª\8eÀ\8c±\82Ì\82½\82ß\82Ì\88ê\8e\9e\93I\82È\82à\82Ì
-                       end else begin
-       // \83\8d\83O\82É\91\8dÝ\82µ\82È\82¢\82Ì\82Å\82±\82Ì\82Ü\82Ü\8fI\97¹
-        Result := '';
-       Exit;
-      end;
-    end;
-               res                     := FDat[ inNo - 1 ];
-               tmp                     := InternalAbon( PChar( res ) );
                Result  := Dat2HTML( string( tmp ), inNo );
+       finally
                DisposeResultString( tmp );
-       except
        end;
 
 end;
@@ -408,22 +603,19 @@ begin
 
 
        // GetRes \82ð\8cÄ\82Î\82ê\82é\82±\82Æ\82ª\97\\91z\82³\82ê\82é\82Ì\82Å FDat \82ð\90\90¬\82µ\82Ä\82¨\82­
-       try
-               if FDat <> nil then begin
-                       try
-                               FDat.Free;
-                               FDat := nil;
-                       except
-                       end;
+       if FDat <> nil then begin
+               try
+                       FDat.Free;
+                       FDat := nil;
+               except
                end;
-               if IsLogFile then begin
-                       // dat \82Ì\93Ç\82Ý\8d\9e\82Ý
-                       FDat                    := TStringList.Create;
-                       FDat.LoadFromFile( FilePath );
+       end;
+       if IsLogFile then begin
+               // dat \82Ì\93Ç\82Ý\8d\9e\82Ý
+               FDat                    := TStringList.Create;
+               FDat.LoadFromFile( FilePath );
 
-                       AllResCount := FDat.Count; // \96{\93\96\82Í\82±\82±\82Å\82â\82é\82×\82«\82Å\82Í\82È\82¢\82ª\8eÀ\8c±\82Ì\82½\82ß\82Ì\88ê\8e\9e\93I\82È\82à\82Ì
-               end;
-       except
+               AllResCount := FDat.Count; // \96{\93\96\82Í\82±\82±\82Å\82â\82é\82×\82«\82Å\82Í\82È\82¢\82ª\8eÀ\8c±\82Ì\82½\82ß\82Ì\88ê\8e\9e\93I\82È\82à\82Ì
        end;
 
 end;
@@ -557,9 +749,181 @@ begin
 
 end;
 
+// =========================================================================
+// TShitarabaBoardItem
+// =========================================================================
+
 // *************************************************************************
-// \83G\83\93\83g\83\8a\83|\83C\83\93\83g
+// \83R\83\93\83X\83g\83\89\83N\83^
+// *************************************************************************
+constructor TShitarabaBoardItem.Create(
+       inInstance      : DWORD
+);
+var
+       uri                                     : TIdURI;
+       uriList                 : TStringList;
+begin
+
+       inherited;
+
+       OnDownload              := Download;
+       OnEnumThread    := EnumThread;
+
+       FilePath                        := '';
+       FIsTemporary    := False;
+       FDat                                    := nil;
+
+       if Copy( URL, Length( URL ), 1 ) = '/' then
+               uri := TIdURI.Create( URL )
+       else
+               uri := TIdURI.Create( URL + '/' );
+       uriList := TStringList.Create;
+       try
+               YofUtils.ExtractHttpFields( ['/', '?'], [], uri.Path, uriList );
+               // http://jbbs.shitaraba.com/game/1000/subject.txt
+               FilePath        := MyLogFolder + uriList[ 1 ] + '\' + uriList[ 2 ] + '\' + uri.Document;
+               IsLogFile       := FileExists( FilePath );
+       finally
+               uri.Free;
+               uriList.Free;
+       end;
+
+end;
+// *************************************************************************
+// \83f\83X\83g\83\89\83N\83^
 // *************************************************************************
+destructor TShitarabaBoardItem.Destroy;
+begin
+
+       if FDat <> nil then begin
+               try
+                       FDat.Free;
+                       FDat := nil;
+               except
+               end;
+       end;
+
+       // \88ê\8e\9e\83t\83@\83C\83\8b\82Ì\8fê\8d\87\82Í\8dí\8f\9c\82·\82é
+       if FIsTemporary then
+               DeleteFile( FilePath );
+
+       inherited;
+
+end;
+
+// *************************************************************************
+// \8ew\92è\82µ\82½\83X\83\8c\88ê\97\97\82Ì\83_\83E\83\93\83\8d\81[\83h\82ð\97v\8b\81\82³\82ê\82½
+// *************************************************************************
+function TShitarabaBoardItem.Download : TDownloadState;
+var
+       modified                        : Double;
+       conv                                    : TPzConv;
+       downResult              : PChar;
+       responseCode    : Longint;
+       subject                         : TStringList;
+       uri                                             : TIdURI;
+       uriList                         : TStringList;
+const
+       SUBJECT_NAME    = 'subject.txt';
+begin
+
+       Result := dsError;
+
+       conv := TPzConv.Create( nil );
+       if Copy( URL, Length( URL ), 1 ) = '/' then
+               uri := TIdURI.Create( URL + SUBJECT_NAME )
+       else
+               uri := TIdURI.Create( URL );
+       uriList := TStringList.Create;
+       subject := TStringList.Create;
+       // \93Æ\8e©\82É\83_\83E\83\93\83\8d\81[\83h\82â\83t\83B\83\8b\83^\83\8a\83\93\83O\82ð\8ds\82í\82È\82¢\8fê\8d\87\82Í
+       // InternalDownload \82É\94C\82¹\82é\82±\82Æ\82ª\8fo\97\88\82é
+       modified := LastModified;
+       responseCode := InternalDownload( PChar( uri.URI ), modified, downResult );
+       try
+               if responseCode = 200 then begin
+                       try
+                               // \83p\83X\82ð\8eZ\8fo
+                               YofUtils.ExtractHttpFields( ['/', '?'], [], uri.Path, uriList );
+                               if MyLogFolder = '' then begin
+                                       // \82Ç\82±\82É\95Û\91\82µ\82Ä\82¢\82¢\82Ì\82©\95ª\82©\82ç\82È\82¢\82Ì\82Å\88ê\8e\9e\83t\83@\83C\83\8b\82É\95Û\91
+                                       FilePath                        := TemporaryFile;
+                                       FIsTemporary    := True;
+                               end else begin
+                                       FilePath                        := MyLogFolder + uriList[ 1 ] + '\' + uriList[ 2 ] + '\' + uri.Document;
+                                       FIsTemporary    := False
+                               end;
+
+                               // \95Û\91\97p\82Ì\83f\83B\83\8c\83N\83g\83\8a\82ð\8c@\82é
+                               ForceDirectoriesEx( Copy( FilePath, 1, LastDelimiter( '\', FilePath ) ) );
+
+                               // EUC \82ð Shift_JIS \82É
+                               subject.Text := conv.EucToSjis( string( downResult ) );
+                               // \95Û\91
+                               subject.SaveToFile( FilePath );
+                       finally
+                               conv.Free;
+                               uri.Free;
+                               uriList.Free;
+                               subject.Free;
+                       end;
+                       Result := dsComplete;
+               end;
+       finally
+               DisposeResultString( downResult );
+       end;
+
+end;
+
+// *************************************************************************
+// \82±\82Ì\94Â\82É\82¢\82­\82Â\82Ì\83X\83\8c\82ª\82 \82é\82©\97v\8b\81\82³\82ê\82½
+// *************************************************************************
+procedure      TShitarabaBoardItem.EnumThread(
+       inCallBack : TBoardItemEnumThreadCallBack
+);
+begin
+
+       if FDat <> nil then
+               inherited EnumThread( inCallBack, FDat.Text );
+
+end;
+
+// *************************************************************************
+// TBoardItem \82ª\90\90¬\82³\82ê\82½\8fê\8d\87\82Ì\8f\88\92u(TShitarabaBoardItem \82ð\90\90¬\82·\82é)
+// *************************************************************************
+procedure BoardItemOnCreateOfTShitarabaBoardItem(
+       inInstance : DWORD
+);
+var
+       boardItem : TShitarabaBoardItem;
+begin
+
+       boardItem := TShitarabaBoardItem.Create( inInstance );
+       BoardItemSetLong( inInstance, bipContext, DWORD( boardItem ) );
+
+end;
+
+// *************************************************************************
+// TBoardItem \82ª\94j\8aü\82³\82ê\82½\8fê\8d\87\82Ì\8f\88\92u(TShitarabaBoardItem \82ð\94j\8aü\82·\82é)
+// *************************************************************************
+procedure BoardItemOnDisposeOfTShitarabaBoardItem(
+       inInstance : DWORD
+);
+var
+       boardItem : TShitarabaBoardItem;
+begin
+
+       boardItem := TShitarabaBoardItem( BoardItemGetLong( inInstance, bipContext ) );
+       boardItem.Free;
+
+end;
+
+
+
+// =========================================================================
+// \83G\83\93\83g\83\8a\83|\83C\83\93\83g
+// =========================================================================
 procedure DLLEntry(
        ul_reason_for_call : DWORD
 );
@@ -570,15 +934,21 @@ begin
        case ul_reason_for_call of
                DLL_PROCESS_ATTACH:
                begin
+                       Randomize;
+
                        module := GetModuleHandle( nil );
 
                        LoadInternalAPI( module );
                        LoadInternalFilePathAPI( module );
                        LoadInternalThreadItemAPI( module );
+                       LoadInternalBoardItemAPI( module );
 
                        // ===== \83C\83\93\83X\83^\83\93\83X\82Ì\8eæ\82è\88µ\82¢\82ð TThreadItem \82©\82ç TShitarabaThreadItem \82É\95Ï\8dX\82·\82é
                        ThreadItemOnCreate      := ThreadItemOnCreateOfTShitarabaThreadItem;
                        ThreadItemOnDispose     := ThreadItemOnDisposeOfTShitarabaThreadItem;
+                       // ===== \83C\83\93\83X\83^\83\93\83X\82Ì\8eæ\82è\88µ\82¢\82ð TBoardItem \82©\82ç TShitarabaBoardItem \82É\95Ï\8dX\82·\82é
+                       BoardItemOnCreate               := BoardItemOnCreateOfTShitarabaBoardItem;
+                       BoardItemOnDispose      := BoardItemOnDisposeOfTShitarabaBoardItem;
                end;
                DLL_PROCESS_DETACH:
                        ;
@@ -593,11 +963,15 @@ end;
 exports
        OnVersionInfo,
        OnAcceptURL,
-       OnDownloadList;
+       OnDownloadList,
+       OnReadList,
+       OnListURL2ThreadURL;
 
 begin
 
-       DllProc := @DLLEntry;
-       DLLEntry( DLL_PROCESS_ATTACH );
+       try
+               DllProc := @DLLEntry;
+               DLLEntry( DLL_PROCESS_ATTACH );
+       except end;
 
 end.