OSDN Git Service

・最新のプラグインインターフェースに対応。
authoryoffy <yoffy>
Sat, 15 Nov 2003 17:52:00 +0000 (17:52 +0000)
committeryoffy <yoffy>
Sat, 15 Nov 2003 17:52:00 +0000 (17:52 +0000)
res/ExternalBoardPlugIn/ExternalBoardPlugIn.dpr

index fed6045..2dddbd5 100644 (file)
@@ -11,34 +11,130 @@ uses
        YofUtils in '..\..\YofUtils.pas',
        PlugInMain in 'PlugInMain.pas',
        ThreadItem in 'ThreadItem.pas',
+       BoardItem in 'BoardItem.pas',
        FilePath in 'FilePath.pas';
 
 {$R *.res}
 
 type
+       // =========================================================================
+       // T2chThreadItem
+       // =========================================================================
        T2chThreadItem = 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;
+       end;
+
+       // =========================================================================
+       // T2chBoardItem
+       // =========================================================================
+       T2chBoardItem = class(TBoardItem)
+       private
+               FIsTemporary    : Boolean;
+               FDat                                    : TStringList;
+
+       public
+               constructor     Create( inInstance : DWORD );
+               destructor      Destroy; override;
+
+       private
+               function        Download : TDownloadState;
+               function        ToThreadURL( inFileName : string ) : string;
+               procedure       EnumThread( inCallBack : TBoardItemEnumThreadCallBack );
+
+       end;
+
+       // =========================================================================
+       // \83T\83u\83W\83F\83N\83g\83\8c\83R\81[\83h
+       // =========================================================================
+       TSubjectRec = record
+               FFileName: string;
+               FTitle: string;
+               FCount: Integer;
        end;
 
 const
-       PLUGIN_NAME                             = 'External2chReader';
+       LOG_DIR                                         = '2ch\';
+
+       PLUGIN_NAME                             = '2chPlugIn';
        MAJOR_VERSION                   = 1;
        MINOR_VERSION                   = 0;
-       RELEASE_VERSION         = 'develop';
+       RELEASE_VERSION         = 'alpha';
        REVISION_VERSION        = 1;
 
+// =========================================================================
+// \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¿BBS\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;
+
+
+
 // *************************************************************************
 // \83v\83\89\83O\83C\83\93\82Ì\83o\81[\83W\83\87\83\93\82ð\97v\8b\81\82³\82ê\82½
 // *************************************************************************
@@ -51,9 +147,6 @@ procedure OnVersionInfo(
 ); stdcall;
 begin
 
-  {$IFDEF DEBUG}
-  Writeln('OnVersionInfo');
-  {$ENDIF}
        outAgent                := CreateResultString( PChar( PLUGIN_NAME ) );
        outMajor                := MAJOR_VERSION;
        outMinor                := MINOR_VERSION;
@@ -75,9 +168,6 @@ const
        BBS_HOST = '2ch.net';
 begin
 
-  {$IFDEF DEBUG}
-  Writeln('OnAcceptURL');
-  {$ENDIF}
        // \97á\82Æ\82µ\82Ä\83z\83X\83g\96¼\82ª 2ch.net \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
@@ -89,25 +179,11 @@ begin
 
 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
-var
-       modified        : Double;
-begin
 
-  {$IFDEF DEBUG}
-  Writeln('OnDownloadList');
-  {$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 );
 
-end;
+// =========================================================================
+// T2chThreadItem
+// =========================================================================
 
 // *************************************************************************
 // \83R\83\93\83X\83g\83\89\83N\83^
@@ -129,14 +205,13 @@ begin
 
        FilePath                        := '';
        FIsTemporary    := False;
+       FDat                                    := nil;
 
-       Randomize;
-
-       uri := TIdURI.Create( URL );
-       uriList := TStringList.Create;
+       uri                     := TIdURI.Create( URL );
+       uriList := TStringList.Create;
        try
                YofUtils.ExtractHttpFields( ['/', '?'], [], uri.Path, uriList );
-               FilePath        := LogFolder + '2ch\' + uriList.Strings[ 3 ] + '\' + uriList.Strings[ 4 ] + '.dat';
+               FilePath        := MyLogFolder + '2ch\' + uriList.Strings[ 3 ] + '\' + uriList.Strings[ 4 ] + '.dat';
                IsLogFile       := FileExists( FilePath );
        finally
                uri.Free;
@@ -151,8 +226,13 @@ end;
 destructor T2chThreadItem.Destroy;
 begin
 
-       if FDat <> nil then
-               FDat.Free;
+       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
@@ -163,44 +243,31 @@ 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;
-
-// *************************************************************************
 // \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 T2chThreadItem.Download;
+function T2chThreadItem.Download : TDownloadState;
 var
        modified                        : Double;
-       tmp                                             : PChar;
+       tmp, tmp2                       : PChar;
+       tmpLen                          : Integer;
        responseCode    : Longint;
        logStream                       : TFileStream;
        uri                                             : TIdURI;
        uriList                         : TStringList;
        datURL                          : string;
+       lastChar                        : Char;
+       downResult              : TStringList;
 begin
 
-  {$IFDEF DEBUG}
-  Writeln('Download');
-  {$ENDIF}
+       Result := dsError;
+
        uri := TIdURI.Create( URL );
        uriList := TStringList.Create;
        try
                YofUtils.ExtractHttpFields( ['/', '?'], [], uri.Path, uriList );
                // http://pc2.2ch.net/test/read.cgi/software/1065250336/l50
                // protocol://host/1/2/3/4/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;
@@ -208,42 +275,79 @@ begin
                        FilePath                        := LogFolder + '2ch\' + uriList.Strings[ 3 ] + '\' + uriList.Strings[ 4 ] + '.dat';
                end;
 
-       if FileExists( FilePath ) then
-               logStream := TFileStream.Create( FilePath, fmOpenReadWrite or fmShareDenyWrite )
-       else
-               logStream := TFileStream.Create( FilePath, fmCreate or fmShareDenyWrite );
-       try
-               // \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;
-               // http://pc2.2ch.net/software/1000000000.dat
-               datURL          :=
-                       uri.Protocol + '://' + uri.Host + '/' +
-                       uriList.Strings[ 3 ] + '/dat/' + uriList.Strings[ 4 ] + '.dat';
-                       responseCode := InternalDownload( PChar( datURL ), modified, tmp, Max( 0, logStream.Size - 1 ) );
-
-               {$IFDEF DEBUG}
-               Writeln( 'responseCode:' + IntToStr( responseCode ) );
-               {$ENDIF}
-               if responseCode = 200 then begin
-                       LastModified            := modified;
-                       IsLogFile                               := True;
-
-                       logStream.Position := Max( 0, logStream.Size - 1 );
-                       logStream.Write( tmp^, StrLen( tmp ) );
-               end;
-
-               DisposeResultString( tmp );
-       finally
-               logStream.Free;
-       end;
+               // \95Û\91\97p\82Ì\83f\83B\83\8c\83N\83g\83\8a\82ð\8c@\82é
+               ForceDirectoriesEx( Copy( FilePath, 1, LastDelimiter( '\', FilePath ) ) );
+
+               if FileExists( FilePath ) then
+                       logStream := TFileStream.Create( FilePath, fmOpenReadWrite or fmShareDenyWrite )
+               else
+                       logStream := TFileStream.Create( FilePath, fmCreate or fmShareDenyWrite );
+               try
+                       // \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;
+                       // http://pc2.2ch.net/software/1000000000.dat
+                       datURL          :=
+                               uri.Protocol + '://' + uri.Host + '/' +
+                               uriList.Strings[ 3 ] + '/dat/' + uriList.Strings[ 4 ] + '.dat';
+                       responseCode := InternalDownload( PChar( datURL ), modified, tmp, Max( 0, logStream.Size - 1 ) );
+
+                       try
+                               if responseCode = 200 then begin
+                                       downResult := TStringList.Create;
+                                       try
+                                               tmpLen                                  := StrLen( tmp );
+
+                                               IsLogFile                               := True;
+
+                                               if Count = 0 then begin
+                                                       Result := dsComplete;
+                                               end else begin
+                                                       Result := dsDiffComplete;
+                                                       logStream.Position := Max( 0, logStream.Size - 1 );
+                                                       logStream.Read( lastChar, 1 );
+                                               end;
+                                               if (Count = 0) or (lastChar = tmp^) then begin
+                                                       downResult.Text := string( tmp + 1 ); // \83J\83E\83\93\83g\82Ì\82½\82ß\82¾\82¯
+                                                       logStream.Write( (tmp + 1)^, tmpLen - 1 );
+
+                                                       LastModified    := modified;
+                                                       NewReceive              := Count + 1;
+                                                       Count                                   := Count + downResult.Count;
+                                                       NewResCount             := downResult.Count;
+                                               end else begin
+                                                       Result                          := dsAbort;
+                                                       modified                        := LastModified;
+                                                       responseCode    := InternalDownload(
+                                                               PChar( datURL ), modified, tmp2, 0, logStream.Size - 1 );
+                                                       logStream.Position := 0;
+                                                       logStream.Write( tmp2, StrLen( tmp2 ) );
+                                                       logStream.Write( tmp, tmpLen );
+
+                                                       downResult.Text := string( tmp2 ) + string( tmp ); // \83J\83E\83\93\83g\82Ì\82½\82ß\82¾\82¯
+
+                                                       LastModified    := modified;
+                                                       if downResult.Count > Count then
+                                                               NewReceive := Count + 1
+                                                       else
+                                                               NewReceive := 1;
+                                                       Count                                   := downResult.Count;
+                                                       NewResCount             := Count - NewReceive + 1;
+                                               end;
+                                       finally
+                                               downResult.Free;
+                                       end;
+                               end;
+                       finally
+                               DisposeResultString( tmp );
+                       end;
+               finally
+                       logStream.Free;
+               end;
        finally
                uri.Free;
                uriList.Free;
        end;
-  {$IFDEF DEBUG}
-  Writeln('Download \82É\97á\8aO\82È\82µ');
-  {$ENDIF}
 
 end;
 
@@ -258,17 +362,25 @@ var
        tmp                     : PChar;
 begin
 
-  {$IFDEF DEBUG}
-  Writeln('GetRes');
-  {$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 );
+               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
-               res                     := FDat[ inNo - 1 ];
-               tmp                     := InternalAbon( PChar( res ) );
                Result  := Dat2HTML( string( tmp ), inNo );
+       finally
                DisposeResultString( tmp );
-       except
        end;
 
 end;
@@ -281,22 +393,25 @@ function T2chThreadItem.GetHeader(
 ) : string;
 begin
 
-  {$IFDEF DEBUG}
-  Writeln('GetHeader');
-  {$ENDIF}
        // \93Æ\8e©\82É\83t\83B\83\8b\83^\83\8a\83\93\83O\82ð\8ds\82í\82È\82¢\8fê\8d\87\82Í
        // InternalHeader \82É\94C\82¹\82é\82±\82Æ\82ª\8fo\97\88\82é
-       Result := InternalHeader( inOptionalHeader );
+       Result := InternalHeader(
+               '<meta http-equiv="Content-type" content="text/html; charset=Shift_JIS">' +
+               inOptionalHeader );
+
 
        // 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
+       if FDat <> nil then begin
+               try
                        FDat.Free;
-               if IsLogFile then begin
-                       FDat := TStringList.Create;
-                       FDat.LoadFromFile( FilePath );
+                       FDat := nil;
+               except
                end;
-       except
+       end;
+       if IsLogFile then begin
+               // dat \82Ì\93Ç\82Ý\8d\9e\82Ý
+               FDat                    := TStringList.Create;
+               FDat.LoadFromFile( FilePath );
        end;
 
 end;
@@ -309,17 +424,16 @@ function T2chThreadItem.GetFooter(
 ) : string;
 begin
 
-  {$IFDEF DEBUG}
-  Writeln('GetFooter');
-  {$ENDIF}
        // \93Æ\8e©\82É\83t\83B\83\8b\83^\83\8a\83\93\83O\82ð\8ds\82í\82È\82¢\8fê\8d\87\82Í
        // InternalFooter \82É\94C\82¹\82é\82±\82Æ\82ª\8fo\97\88\82é
        Result := InternalFooter( inOptionalFooter );
 
        // \82à\82¤ GetRes \82Í\8cÄ\82Î\82ê\82È\82¢\82Æ\8ev\82¤\82Ì\82Å FDat \82ð\8aJ\95ú\82µ\82Ä\82¨\82­
        try
-               if FDat <> nil then
+               if FDat <> nil then begin
                        FDat.Free;
+                       FDat := nil;
+               end;
        except
        end;
 
@@ -355,9 +469,253 @@ begin
 
 end;
 
+// =========================================================================
+// T2chBoardItem
+// =========================================================================
+
 // *************************************************************************
-// \83G\83\93\83g\83\8a\83|\83C\83\93\83g
+// \83R\83\93\83X\83g\83\89\83N\83^
+// *************************************************************************
+constructor T2chBoardItem.Create(
+       inInstance      : DWORD
+);
+var
+       uri                                     : TIdURI;
+       uriList                 : TStringList;
+begin
+
+       inherited;
+
+       OnDownload                                              := Download;
+       OnEnumThread                                    := EnumThread;
+       OnFileName2ThreadURL    := ToThreadURL;
+
+       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://2ch.net/hoge/subject.txt
+               FilePath        := MyLogFolder + uriList[ 1 ] + '\' + uri.Document;
+               IsLogFile       := FileExists( FilePath );
+       finally
+               uri.Free;
+               uriList.Free;
+       end;
+
+end;
+// *************************************************************************
+// \83f\83X\83g\83\89\83N\83^
+// *************************************************************************
+destructor T2chBoardItem.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 T2chBoardItem.Download : TDownloadState;
+var
+       modified                        : Double;
+       downResult              : PChar;
+       responseCode    : Longint;
+       uri                                             : TIdURI;
+       uriList                         : TStringList;
+const
+       SUBJECT_NAME    = 'subject.txt';
+begin
+
+       Result := dsError;
+
+       if Copy( URL, Length( URL ), 1 ) = '/' then
+               uri := TIdURI.Create( URL + SUBJECT_NAME )
+       else
+               uri := TIdURI.Create( URL );
+       uriList := TStringList.Create;
+       if FDat <> nil then begin
+               try
+                       FDat.Free;
+                       FDat := nil;
+               except
+               end;
+       end;
+       FDat := 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 ] + '\' + 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 ) ) );
+
+                               FDat.Text := string( downResult );
+                               // \95Û\91
+                               FDat.SaveToFile( FilePath );
+                       finally
+                               uri.Free;
+                               uriList.Free;
+                       end;
+                       Result := dsComplete;
+               end;
+       finally
+               DisposeResultString( downResult );
+       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 T2chBoardItem.ToThreadURL(
+       inFileName      : string        // \83X\83\8c\83b\83h\83t\83@\83C\83\8b\96¼
+) : string;                                                    // \83X\83\8c\83b\83h\82Ì URL
+var
+       threadURL               : string;
+       uri                                     : TIdURI;
+       uriList                 : TStringList;
+       found                           : Integer;
+begin
+
+       found := Pos( '.', inFileName );
+       if found > 0 then
+               inFileName := Copy( inFileName, 1, found - 1 );
+       if Copy( URL, Length( URL ), 1 ) = '/' then
+               uri := TIdURI.Create( URL )
+       else
+               uri := TIdURI.Create( URL + '/' );
+       uriList := TStringList.Create;
+
+       try
+               try
+                       // http://book.2ch.net/bizplus/subject.txt
+                       // http://book.2ch.net/test/read.cgi/bizplus/1068905348/l50
+                       YofUtils.ExtractHttpFields( ['/', '?'], [], uri.Path, uriList );
+                       threadURL       := uri.Protocol + '://' + uri.Host + '/test/read.cgi/' +
+                               uriList[ 1 ] + '/' + inFileName + '/l50';
+                       Result          := threadURL;
+               finally
+                       uri.Free;
+                       uriList.Free;
+               end;
+       except
+               Result := '';
+       end;
+
+end;
+
+// *************************************************************************
+// \82±\82Ì\94Â\82É\82¢\82­\82Â\82Ì\83X\83\8c\82ª\82 \82é\82©\97v\8b\81\82³\82ê\82½
+// *************************************************************************
+procedure      T2chBoardItem.EnumThread(
+       inCallBack      : TBoardItemEnumThreadCallBack
+);
+var
+       uri                                     : TIdURI;
+       uriList                 : TStringList;
+const
+       SUBJECT_NAME    = 'subject.txt';
+begin
+
+       try
+               if FDat = nil then begin
+                       FDat := TStringList.Create;
+
+                       if Copy( URL, Length( URL ), 1 ) = '/' then
+                               uri := TIdURI.Create( URL + SUBJECT_NAME )
+                       else
+                               uri := TIdURI.Create( URL );
+                       uriList := TStringList.Create;
+                       try
+                               // \83p\83X\82ð\8eZ\8fo
+                               YofUtils.ExtractHttpFields( ['/', '?'], [], uri.Path, uriList );
+                               // http://book.2ch.net/bizplus/subject.txt
+                               FilePath        := MyLogFolder + uriList[ 1 ] + '\' + uri.Document;
+                               if FileExists( FilePath ) then
+                                       // \93Ç\82Ý\8d\9e\82Ý
+                                       FDat.LoadFromFile( FilePath );
+                       finally
+                               uri.Free;
+                               uriList.Free;
+                       end;
+               end;
+
+               // \93Æ\8e©\82É\83t\83B\83\8b\83^\83\8a\83\93\83O\82ð\8ds\82í\82È\82¢\8fê\8d\87\82Í EnumThread \82É\94C\82¹\82é\82±\82Æ\82ª\8fo\97\88\82é
+               inherited EnumThread( inCallBack, FDat.Text );
+       except
+       end;
+
+end;
+
+// *************************************************************************
+// TBoardItem \82ª\90\90¬\82³\82ê\82½\8fê\8d\87\82Ì\8f\88\92u(T2chBoardItem \82ð\90\90¬\82·\82é)
+// *************************************************************************
+procedure BoardItemOnCreateOfT2chBoardItem(
+       inInstance : DWORD
+);
+var
+       boardItem : T2chBoardItem;
+begin
+
+       boardItem := T2chBoardItem.Create( inInstance );
+       BoardItemSetLong( inInstance, bipContext, DWORD( boardItem ) );
+
+end;
+
 // *************************************************************************
+// TBoardItem \82ª\94j\8aü\82³\82ê\82½\8fê\8d\87\82Ì\8f\88\92u(T2chBoardItem \82ð\94j\8aü\82·\82é)
+// *************************************************************************
+procedure BoardItemOnDisposeOfT2chBoardItem(
+       inInstance : DWORD
+);
+var
+       boardItem : T2chBoardItem;
+begin
+
+       boardItem := T2chBoardItem( BoardItemGetLong( inInstance, bipContext ) );
+       boardItem.Free;
+
+end;
+
+
+
+// =========================================================================
+// \83G\83\93\83g\83\8a\83|\83C\83\93\83g
+// =========================================================================
 procedure DLLEntry(
        ul_reason_for_call : DWORD
 );
@@ -368,15 +726,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ç T2chThreadItem \82É\95Ï\8dX\82·\82é
                        ThreadItemOnCreate      := ThreadItemOnCreateOfT2chThreadItem;
                        ThreadItemOnDispose     := ThreadItemOnDisposeOfT2chThreadItem;
+                       // ===== \83C\83\93\83X\83^\83\93\83X\82Ì\8eæ\82è\88µ\82¢\82ð TBoardItem \82©\82ç T2chBoardItem \82É\95Ï\8dX\82·\82é
+                       BoardItemOnCreate               := BoardItemOnCreateOfT2chBoardItem;
+                       BoardItemOnDispose      := BoardItemOnDisposeOfT2chBoardItem;
                end;
                DLL_PROCESS_DETACH:
                        ;
@@ -390,8 +754,7 @@ end;
 
 exports
        OnVersionInfo,
-       OnAcceptURL,
-       OnDownloadList;
+       OnAcceptURL;
 
 begin