OSDN Git Service

・板がない場合は IE で開くように変更。
[gikonavigoeson/gikonavi.git] / GikoSystem.pas
index bfe38a2..ffc398b 100644 (file)
@@ -13,7 +13,8 @@ uses
        MSHTML_TLB,
 {$IFEND}
        {HttpApp,} YofUtils, URLMon, IdGlobal, IdURI, {Masks,}
-       Setting, BoardGroup, gzip, Dolib, bmRegExp, AbonUnit;
+       Setting, BoardGroup, gzip, Dolib, bmRegExp, AbonUnit,
+       MojuUtils, ExternalBoardManager, ExternalBoardPlugInMain;
 
 type
        //BBS\83^\83C\83v
@@ -91,7 +92,6 @@ type
                FOnlyAHundredRes : Boolean;
 //             FExitWrite: TStringList;
 //             function StrToFloatDef(s: string; Default: Double): Double;
-
        public
                { Public \90é\8c¾ }
                FAbon : TAbon;
@@ -125,7 +125,6 @@ type
                function GetSkinNewmarkFileName: string;
                function GetStyleSheetDir: string;
                function GetOutBoxFileName: string;
-               function GetURL(BBSID: string; FileName: string): string;
                function GetUserAgent: string;
 
                procedure ReadSubjectFile(Board: TBoard);
@@ -184,6 +183,14 @@ type
                function Parse2chURL2(URL: string): TPathRec;
                procedure ParseURI(var URL, Protocol, Host, Path, Document, Port, Bookmark: string);
                function GetVersionBuild: Integer;
+               function        GetThreadURL2BoardURL( inURL : string ) : string;
+               function        Get2chThreadURL2BoardURL( inURL : string ) : string;
+               function        Get2chBrowsableThreadURL( inURL : string ) : string;
+               function        Get2chBoard2ThreadURL( inBoard : TBoard; inKey : string ) : string;
+               procedure ReadBoardFile;
+
+               function        GetUnknownCategory : TCategory;
+               function        GetUnknownBoard( inPlugIn : TBoardPlugIn; inURL : string ) : TBoard;
 
                // \83X\83L\83\93\82ð\93Ç\82Ý\8d\9e\82Ý\81A\92l\82ð\92u\8a·\82·\82é
                function LoadFromSkin( fileName: string; ThreadItem: TThreadItem; SizeByte: Integer ): string;
@@ -206,7 +213,7 @@ const
 implementation
 
 uses
-       Giko, RoundData, ExternalBoardManager;
+       Giko, RoundData;
 
 const
        FOLDER_INDEX_VERSION                                    = '1.01';
@@ -214,6 +221,10 @@ const
        DEFAULT_NGWORD_FILE_NAME : String = 'NGword.txt';
        NGWORDs_DIR_NAME : String               = 'NGwords';
 
+       READ_PATH: string =                     '/test/read.cgi/';
+       OLD_READ_PATH: string =         '/test/read.cgi?';
+       KAKO_PATH: string =                     '/kako/';
+
 (*************************************************************************
  *GikoSys\83R\83\93\83X\83g\83\89\83N\83^
  *************************************************************************)
@@ -256,7 +267,8 @@ begin
        FAWKStr.Free;
        FSetting.Free;
        FDolib.Free;
-
+               FAbon.Free;
+               FSelectResFilter.Free;
        //\83e\83\93\83|\83\89\83\8aHTML\82ð\8dí\8f\9c
        FileList := TStringList.Create;
        try
@@ -399,19 +411,6 @@ begin
        Result := Setting.GetSkinNewmarkFileName;
 end;
 
-(*************************************************************************
- *URL\82ð\8dì\90¬(\83R\83s\83y\97p)
- *************************************************************************)
-function TGikoSys.GetURL(BBSID: string; FileName: string): string;
-var
-       Board: TBoard;
-begin
-       Board := BoardGroup.BBS2ch.GetBoardFromBBSID(BBSID);
-       Result := UrlToServer(Board.URL) + 'test/read.cgi/' + UrlToID(Board.URL) + '/' + ChangeFileExt(FileName, '') + '/l50';
-       //http://teri.2ch.net/test/read.cgi?bbs=accuse&key=974619522&ls=50
-       //http://pc.2ch.net/test/read.cgi/tech/1003664165/l50
-end;
-
 // UserAgent\8eæ\93¾
 function TGikoSys.GetUserAgent: string;
 begin
@@ -523,14 +522,13 @@ begin
        //\91O\89ñ\88Ù\8fí\8fI\97¹\8e\9e\97pTmp\83t\83@\83C\83\8b\83\8a\83X\83g
        GetFileList(ExtractFileDir(Board.GetFolderIndexFileName), '*.tmp', TmpFileList, False, False);
 
-
        sl := TStringList.Create;
        try
                if FileExists(FileName) then
                        sl.LoadFromFile(FileName);
 
                //\82Q\8ds\96Ú\82©\82ç\81i\82P\8ds\96Ú\82Í\83o\81[\83W\83\87\83\93\81j
-               for i := 1 to sl.Count - 1 do begin
+               for i := sl.Count - 1 downto 1 do begin
                        Rec := ParseIndexLine(sl[i]);
 
                        if usePlugIn then
@@ -538,7 +536,15 @@ begin
                                        Board.BoardPlugIn,
                                        Board.BoardPlugIn.FileName2ThreadURL( DWORD( Board ), Rec.FFileName ) )
                        else
-                               ThreadItem := TThreadItem.Create;
+                               ThreadItem := TThreadItem.Create(
+                                       nil,
+                                       Get2chBoard2ThreadURL( Board, ChangeFileExt( Rec.FFileName, '' ) ) );
+
+                       if FileList.Count <> 0 then
+                               if FileList.Find( ThreadItem.FileName, Index ) then
+                                       //ThreadItem.IsLogFile := True;
+                                       FileList.Delete( Index );
+
                        ThreadItem.BeginUpdate;
                        ThreadItem.No := Rec.FNo;
                        ThreadItem.FileName := Rec.FFileName;
@@ -558,15 +564,6 @@ begin
                        ThreadItem.AgeSage := Rec.FAgeSage;
                        ThreadItem.ParentBoard := Board;
 
-                       //IsLogFile\83`\83F\83b\83N
-                       ThreadItem.IsLogFile := False;
-                       if FileList.Count <> 0 then begin
-                               if FileList.Find(ThreadItem.FileName, Index) then begin
-                                       ThreadItem.IsLogFile := True;
-                                       FileList.Delete(Index);
-                               end;
-                       end;
-
                        //\8f\84\89ñ\83\8a\83X\83g\82É\91\8dÝ\82µ\82½\82ç\8f\84\89ñ\83t\83\89\83O\83Z\83b\83g
                        if ThreadItem.IsLogFile then begin
                                idx := RoundList.Find(ThreadItem);
@@ -621,7 +618,8 @@ begin
                                        Board.BoardPlugIn,
                                        Board.BoardPlugIn.FileName2ThreadURL( DWORD( Board ), Rec.FFileName ) )
                        else
-                               ThreadItem := TThreadItem.Create;
+                               ThreadItem := TThreadItem.Create(
+                                       nil, Get2chBoard2ThreadURL( Board, ChangeFileExt( Rec.FFileName, '' ) ) );
                        ThreadItem.No := Board.Count + 1;
                        ThreadItem.FileName := FileList[i];
                        ThreadItem.Title := ResRec.FTitle;
@@ -723,7 +721,7 @@ begin
        if not Board.IsThreadDatRead then
                Exit;
        FileName := Board.GetFolderIndexFileName;
-       ForceDirectoriesEx(Board.ParentCategory.ParentBBS2ch.GetLogFolder + Board.BBSID);
+       ForceDirectoriesEx( ExtractFilePath( FileName ) );
 
        sl := TStringList.Create;
        try
@@ -873,42 +871,66 @@ begin
                        try
                                if ThreadItem.ParentBoard <> nil then
                                        if ThreadItem.ParentBoard.ParentCategory <> nil then
-                                               Skin.Text := StringReplace( Skin.Text, '<BBSNAME/>', ThreadItem.ParentBoard.ParentCategory.ParentBBS2ch.Title, [rfReplaceAll] );
-                               Skin.Text := StringReplace( Skin.Text, '<THREADURL/>', ThreadItem.URL, [rfReplaceAll] );
+                                                                                        CustomStringReplace( Skin, '<BBSNAME/>', ThreadItem.ParentBoard.ParentCategory.ParenTBBS.Title);
+                                                                                        //Skin.Text := CustomStringReplace( Skin.Text, '<BBSNAME/>', ThreadItem.ParentBoard.ParentCategory.ParenTBBS.Title);
+                               //Skin.Text := CustomStringReplace( Skin.Text, '<THREADURL/>', ThreadItem.URL);
+                                                               CustomStringReplace( Skin, '<THREADURL/>', ThreadItem.URL);
                        except end;
-                       Skin.Text := StringReplace( Skin.Text, '<BOARDNAME/>', ThreadItem.ParentBoard.Title, [rfReplaceAll] );
-                       Skin.Text := StringReplace( Skin.Text, '<BOARDURL/>', ThreadItem.ParentBoard.URL, [rfReplaceAll] );
-                       Skin.Text := StringReplace( Skin.Text, '<THREADNAME/>', ThreadItem.Title, [rfReplaceAll] );
-                       Skin.Text := StringReplace( Skin.Text, '<SKINPATH/>', Setting.CSSFileName, [rfReplaceAll] );
-                       Skin.Text := StringReplace( Skin.Text, '<GETRESCOUNT/>', IntToStr( ThreadItem.NewReceive - 1 ), [rfReplaceAll] );
-                       Skin.Text := StringReplace( Skin.Text, '<NEWRESCOUNT/>', IntToStr( ThreadItem.NewResCount ), [rfReplaceAll] );
-                       Skin.Text := StringReplace( Skin.Text, '<ALLRESCOUNT/>', IntToStr( ThreadItem.AllResCount ), [rfReplaceAll] );
-
-                       Skin.Text := StringReplace( Skin.Text, '<NEWDATE/>',
-                       FormatDateTime('yyyy/mm/dd(ddd) hh:mm', ThreadItem.RoundDate), [rfReplaceAll] );
-                       Skin.Text := StringReplace( Skin.Text, '<SIZEKB/>', IntToStr( Floor( SizeByte / 1024 ) ), [rfReplaceAll] );
-                       Skin.Text := StringReplace( Skin.Text, '<SIZE/>', IntToStr( SizeByte ), [rfReplaceAll] );
+                       //Skin.Text := CustomStringReplace( Skin.Text, '<BOARDNAME/>', ThreadItem.ParentBoard.Title);
+                                               CustomStringReplace( Skin, '<BOARDNAME/>', ThreadItem.ParentBoard.Title);
+                       //Skin.Text := CustomStringReplace( Skin.Text, '<BOARDURL/>', ThreadItem.ParentBoard.URL);
+                                               CustomStringReplace( Skin, '<BOARDURL/>', ThreadItem.ParentBoard.URL);
+                       //Skin.Text := CustomStringReplace( Skin.Text, '<THREADNAME/>', ThreadItem.Title);
+                                               CustomStringReplace( Skin, '<THREADNAME/>', ThreadItem.Title);
+                       //Skin.Text := CustomStringReplace( Skin.Text, '<SKINPATH/>', Setting.CSSFileName);
+                                               CustomStringReplace( Skin, '<SKINPATH/>', Setting.CSSFileName);
+                       //Skin.Text := CustomStringReplace( Skin.Text, '<GETRESCOUNT/>', IntToStr( ThreadItem.NewReceive - 1 ));
+                                               CustomStringReplace( Skin, '<GETRESCOUNT/>', IntToStr( ThreadItem.NewReceive - 1 ));
+                       //Skin.Text := CustomStringReplace( Skin.Text, '<NEWRESCOUNT/>', IntToStr( ThreadItem.NewResCount ));
+                                               CustomStringReplace( Skin, '<NEWRESCOUNT/>', IntToStr( ThreadItem.NewResCount ));
+                       //Skin.Text := CustomStringReplace( Skin.Text, '<ALLRESCOUNT/>', IntToStr( ThreadItem.AllResCount ));
+                                               CustomStringReplace( Skin, '<ALLRESCOUNT/>', IntToStr( ThreadItem.AllResCount ));
+
+                       //Skin.Text := CustomStringReplace( Skin.Text, '<NEWDATE/>',
+                       //FormatDateTime('yyyy/mm/dd(ddd) hh:mm', ThreadItem.RoundDate));
+                                               CustomStringReplace( Skin, '<NEWDATE/>',FormatDateTime('yyyy/mm/dd(ddd) hh:mm', ThreadItem.RoundDate));
+                       //Skin.Text := CustomStringReplace( Skin.Text, '<SIZEKB/>', IntToStr( Floor( SizeByte / 1024 ) ));
+                                               CustomStringReplace( Skin, '<SIZEKB/>', IntToStr( Floor( SizeByte / 1024 ) ));
+                       //Skin.Text := CustomStringReplace( Skin.Text, '<SIZE/>', IntToStr( SizeByte ));
+                                               CustomStringReplace( Skin, '<SIZE/>', IntToStr( SizeByte ));
 
                        //----- \82Æ\82è\82 \82¦\82¸\82©\82¿\82ã\81`\82µ\82á\8cÝ\8a·\97p\81B\83R\83\81\83\93\83g\83A\83E\83g\82µ\82Ä\82à\82æ\82µ
                        // \82â\82è\82©\82½\82ª\8bê\82µ\82¢\82¯\82Ç\81A\83I\83v\83V\83\87\83\93\83_\83C\83A\83\8d\83O\82Ì\83v\83\8c\83r\83\85\81[\97p try
                        try
                                if ThreadItem.ParentBoard <> nil then
                                        if ThreadItem.ParentBoard.ParentCategory <> nil then
-                                               Skin.Text := StringReplace( Skin.Text, '&BBSNAME', ThreadItem.ParentBoard.ParentCategory.ParentBBS2ch.Title, [rfReplaceAll] );
-                               Skin.Text := StringReplace( Skin.Text, '&THREADURL', ThreadItem.URL, [rfReplaceAll] );
+                                                                                               CustomStringReplace( Skin, '&BBSNAME', ThreadItem.ParentBoard.ParentCategory.ParenTBBS.Title);
+                                               //Skin.Text := CustomStringReplace( Skin.Text, '&BBSNAME', ThreadItem.ParentBoard.ParentCategory.ParenTBBS.Title);
+                               //Skin.Text := CustomStringReplace( Skin.Text, '&THREADURL', ThreadItem.URL);
+                                                               CustomStringReplace( Skin, '&THREADURL', ThreadItem.URL);
                        except end;
-                       Skin.Text := StringReplace( Skin.Text, '&BOARDNAME', ThreadItem.ParentBoard.Title, [rfReplaceAll] );
-                       Skin.Text := StringReplace( Skin.Text, '&BOARDURL', ThreadItem.ParentBoard.URL, [rfReplaceAll] );
-                       Skin.Text := StringReplace( Skin.Text, '&THREADNAME', ThreadItem.Title, [rfReplaceAll] );
-                       Skin.Text := StringReplace( Skin.Text, '&SKINPATH', Setting.CSSFileName, [rfReplaceAll] );
-                       Skin.Text := StringReplace( Skin.Text, '&GETRESCOUNT', IntToStr( ThreadItem.NewReceive - 1 ), [rfReplaceAll] );
-                       Skin.Text := StringReplace( Skin.Text, '&NEWRESCOUNT', IntToStr( ThreadItem.NewResCount ), [rfReplaceAll] );
-                       Skin.Text := StringReplace( Skin.Text, '&ALLRESCOUNT', IntToStr( ThreadItem.AllResCount ), [rfReplaceAll] );
-
-                       Skin.Text := StringReplace( Skin.Text, '&NEWDATE',
-                                       FormatDateTime('yyyy/mm/dd(ddd) hh:mm', ThreadItem.RoundDate), [rfReplaceAll] );
-                       Skin.Text := StringReplace( Skin.Text, '&SIZEKB', IntToStr( Floor( SizeByte / 1024 ) ), [rfReplaceAll] );
-                       Skin.Text := StringReplace( Skin.Text, '&SIZE', IntToStr( SizeByte ), [rfReplaceAll] );
+                       //Skin.Text := CustomStringReplace( Skin.Text, '&BOARDNAME', ThreadItem.ParentBoard.Title);
+                                               CustomStringReplace( Skin, '&BOARDNAME', ThreadItem.ParentBoard.Title);
+                       //Skin.Text := CustomStringReplace( Skin.Text, '&BOARDURL', ThreadItem.ParentBoard.URL);
+                                               CustomStringReplace( Skin, '&BOARDURL', ThreadItem.ParentBoard.URL);
+                       //Skin.Text := CustomStringReplace( Skin.Text, '&THREADNAME', ThreadItem.Title);
+                                               CustomStringReplace( Skin, '&THREADNAME', ThreadItem.Title);
+                       //Skin.Text := CustomStringReplace( Skin.Text, '&SKINPATH', Setting.CSSFileName);
+                                               CustomStringReplace( Skin, '&SKINPATH', Setting.CSSFileName);
+                       //Skin.Text := CustomStringReplace( Skin.Text, '&GETRESCOUNT', IntToStr( ThreadItem.NewReceive - 1 ));
+                                               CustomStringReplace( Skin, '&GETRESCOUNT', IntToStr( ThreadItem.NewReceive - 1 ));
+                       //Skin.Text := CustomStringReplace( Skin.Text, '&NEWRESCOUNT', IntToStr( ThreadItem.NewResCount ));
+                                               CustomStringReplace( Skin, '&NEWRESCOUNT', IntToStr( ThreadItem.NewResCount ));
+                       //Skin.Text := CustomStringReplace( Skin.Text, '&ALLRESCOUNT', IntToStr( ThreadItem.AllResCount ));
+                                               CustomStringReplace( Skin, '&ALLRESCOUNT', IntToStr( ThreadItem.AllResCount ));
+
+                       //Skin.Text := CustomStringReplace( Skin.Text, '&NEWDATE',
+                       //              FormatDateTime('yyyy/mm/dd(ddd) hh:mm', ThreadItem.RoundDate));
+                                               CustomStringReplace( Skin, '&NEWDATE', FormatDateTime('yyyy/mm/dd(ddd) hh:mm', ThreadItem.RoundDate));
+                       //Skin.Text := CustomStringReplace( Skin.Text, '&SIZEKB', IntToStr( Floor( SizeByte / 1024 ) ));
+                                               CustomStringReplace( Skin, '&SIZEKB', IntToStr( Floor( SizeByte / 1024 ) ));
+                       //Skin.Text := CustomStringReplace( Skin.Text, '&SIZE', IntToStr( SizeByte ));
+                                               CustomStringReplace( Skin, '&SIZE', IntToStr( SizeByte ));
                        //----- \82±\82±\82Ü\82Å
                end;
                Result := Skin.Text;
@@ -926,26 +948,26 @@ function TGikoSys.SkinedRes(
 begin
 
        try
-               Skin := StringReplace( Skin, '<NUMBER/>',
-                       '<a href="menu:' + No + '" name="' + No + '">' + No + '</a>', [rfReplaceAll] );
-               Skin := StringReplace( Skin, '<PLAINNUMBER/>', No, [rfReplaceAll] );
-               Skin := StringReplace( Skin, '<NAME/>', '<b>' + Res.FName + '</b>', [rfReplaceAll] );
-               Skin := StringReplace( Skin, '<MAILNAME/>',
-                       '<a href="mailo:' + Res.FMailTo + '"><b>' + Res.FName + '</b></a>', [rfReplaceAll] );
-               Skin := StringReplace( Skin, '<MAIL/>', Res.FMailTo, [rfReplaceAll] );
-               Skin := StringReplace( Skin, '<DATE/>', Res.FDateTime, [rfReplaceAll] );
-               Skin := StringReplace( Skin, '<MESSAGE/>', Res.FBody, [rfReplaceAll] );
+               Skin := CustomStringReplace( Skin, '<NUMBER/>',
+                       '<a href="menu:' + No + '" name="' + No + '">' + No + '</a>');
+               Skin := CustomStringReplace( Skin, '<PLAINNUMBER/>', No);
+               Skin := CustomStringReplace( Skin, '<NAME/>', '<b>' + Res.FName + '</b>');
+               Skin := CustomStringReplace( Skin, '<MAILNAME/>',
+                       '<a href="mailo:' + Res.FMailTo + '"><b>' + Res.FName + '</b></a>');
+               Skin := CustomStringReplace( Skin, '<MAIL/>', Res.FMailTo);
+               Skin := CustomStringReplace( Skin, '<DATE/>', Res.FDateTime);
+               Skin := CustomStringReplace( Skin, '<MESSAGE/>', Res.FBody);
 
                //----- \82©\82¿\82ã\81`\82µ\82á\8cÝ\8a·\97p\81B\83R\83\81\83\93\83g\83A\83E\83g\82µ\82Ä\82à\82æ\82µ
-               Skin := StringReplace( Skin, '&NUMBER',
-                       '<a href="menu:' + No + '" name="' + No + '">' + No + '</a>', [rfReplaceAll] );
-               Skin := StringReplace( Skin, '&PLAINNUMBER', No, [rfReplaceAll] );
-               Skin := StringReplace( Skin, '&NAME', '<b>' + Res.FName + '</b>', [rfReplaceAll] );
-               Skin := StringReplace( Skin, '&MAILNAME',
-                       '<a href="mailo:' + Res.FMailTo + '"><b>' + Res.FName + '</b></a>', [rfReplaceAll] );
-               Skin := StringReplace( Skin, '&MAIL', Res.FMailTo, [rfReplaceAll] );
-               Skin := StringReplace( Skin, '&DATE', Res.FDateTime, [rfReplaceAll] );
-               Skin := StringReplace( Skin, '&MESSAGE', Res.FBody, [rfReplaceAll] );
+               Skin := CustomStringReplace( Skin, '&NUMBER',
+                       '<a href="menu:' + No + '" name="' + No + '">' + No + '</a>');
+               Skin := CustomStringReplace( Skin, '&PLAINNUMBER', No);
+               Skin := CustomStringReplace( Skin, '&NAME', '<b>' + Res.FName + '</b>');
+               Skin := CustomStringReplace( Skin, '&MAILNAME',
+                       '<a href="mailo:' + Res.FMailTo + '"><b>' + Res.FName + '</b></a>');
+               Skin := CustomStringReplace( Skin, '&MAIL', Res.FMailTo);
+               Skin := CustomStringReplace( Skin, '&DATE', Res.FDateTime);
+               Skin := CustomStringReplace( Skin, '&MESSAGE', Res.FBody);
                //----- \82±\82±\82Ü\82Å
 
                Result := Skin;
@@ -1070,11 +1092,10 @@ begin
                                        SaveList.Clear;
                                end;
 
-                               SaveList.Add('<a name="bottom"></a>');
                                threadItem.SizeByte := threadItem.SizeByte + Length( SaveList.Text );
 
                                // \83X\83L\83\93(\83t\83b\83^)
-                               SaveList.Add( boardPlugIn.GetFooter( DWORD( threadItem ), '' ) );
+                               SaveList.Add( boardPlugIn.GetFooter( DWORD( threadItem ), '<a name="bottom"></a>' ) );
 
                                doc.Write(SaveList.Text);
                        finally
@@ -1101,10 +1122,10 @@ begin
                        FAbon.Execute(ReadList);                //       \82 \82Ú\81`\82ñ\82µ\82Ä
                        FSelectResFilter.Execute(ReadList); //\83\8c\83X\82Ì\83t\83B\83\8b\83^\83\8a\83\93\83O\82ð\82·\82é
                        Res := DivideStrLine(ReadList[0]);
-                       Res.FTitle := StringReplace(Res.FTitle, '\81\97\81M', ',', [rfReplaceAll]);
+                       Res.FTitle := CustomStringReplace(Res.FTitle, '\81\97\81M', ',');
                        sTitle := Res.FTitle;
                end else begin
-                       sTitle := StringReplace(ThreadItem.Title, '\81\97\81M', ',', [rfReplaceAll]);
+                       sTitle := CustomStringReplace(ThreadItem.Title, '\81\97\81M', ',');
                end;
                SaveList := TStringList.Create;
                try
@@ -1140,8 +1161,8 @@ begin
                                try
                                        SkinHeader := LoadSkin( GetSkinHeaderFileName );
                                        if Length( UserOptionalStyle ) > 0 then
-                                               SkinHeader := StringReplace( SkinHeader, '</head>',
-                                                       '<style type="text/css">body {' + UserOptionalStyle + '}</style></head>', [rfReplaceAll] );
+                                               SkinHeader := CustomStringReplace( SkinHeader, '</head>',
+                                                       '<style type="text/css">body {' + UserOptionalStyle + '}</style></head>');
                                        SaveList.Add( SkinHeader );
                                except
                                end;
@@ -1167,9 +1188,9 @@ begin
                                                Res.FBody := ConvRes(Res.FBody, ThreadItem.ParentBoard.BBSID, ChangeFileExt(ThreadItem.FileName, ''), 'bbs', 'key', 'st', 'to', 'nofirst', 'true');
 
                                                if Res.FType = glt2chOld then begin
-                                                       Res.FMailTo := StringReplace(Res.FMailTo, '\81\97\81M', ',', [rfReplaceAll]);
-                                                       Res.FName := StringReplace(Res.FName, '\81\97\81M', ',', [rfReplaceAll]);
-                                                       Res.FBody := StringReplace(Res.FBody, '\81\97\81M', ',', [rfReplaceAll]);
+                                                       Res.FMailTo := CustomStringReplace(Res.FMailTo, '\81\97\81M', ',');
+                                                       Res.FName := CustomStringReplace(Res.FName, '\81\97\81M', ',');
+                                                       Res.FBody := CustomStringReplace(Res.FBody, '\81\97\81M', ',');
                                                end;
 
                                                Res.FBody := AddAnchorTag(Res.FBody);
@@ -1316,9 +1337,9 @@ begin
                                                Res := DivideStrLine(ReadList[i]);
                                                Res.FBody := ConvRes(Res.FBody, ThreadItem.ParentBoard.BBSID, ChangeFileExt(ThreadItem.FileName, ''), 'bbs', 'key', 'st', 'to', 'nofirst', 'true');
                                                if Res.FType = glt2chOld then begin
-                                                       Res.FMailTo := StringReplace(Res.FMailTo, '\81\97\81M', ',', [rfReplaceAll]);
-                                                       Res.FName := StringReplace(Res.FName, '\81\97\81M', ',', [rfReplaceAll]);
-                                                       Res.FBody := StringReplace(Res.FBody, '\81\97\81M', ',', [rfReplaceAll]);
+                                                       Res.FMailTo := CustomStringReplace(Res.FMailTo, '\81\97\81M', ',');
+                                                       Res.FName := CustomStringReplace(Res.FName, '\81\97\81M', ',');
+                                                       Res.FBody := CustomStringReplace(Res.FBody, '\81\97\81M', ',');
                                                end;
                                                Res.FBody := AddAnchorTag(Res.FBody);
                                                if Res.FMailTo = '' then
@@ -1361,14 +1382,19 @@ const
                                                                         + 'abcdefghijklmnopqrstuvwxyz'
                                                                         + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
                                                                         + '#$%&()*+,-./:;=?@[]^_`{|}~!''\';
+       ANCHOR_REF      = 'href=';
+       RES_REF                 = '&gt;&gt;';
 var
        wkIdx: array[0..9] of Integer;
        url: string;
        href: string;
        i: Integer;
        idx: Integer;
+       anchorLen : Integer;
 begin
        Result := '';
+       // + 3 \82Í 'href="' ('"'\82Â\82«)\82È\82Ç\82Ì\83o\83\8a\83G\81[\83V\83\87\83\93\82É\97]\97T\82ð\8e\9d\82½\82¹\82é\82½\82ß
+       anchorLen := Length( ANCHOR_REF ) + 3;
 
        while True do begin
                wkIdx[0] := AnsiPos('http://', s);
@@ -1383,7 +1409,7 @@ begin
                wkIdx[9] := AnsiPos('rtsp://', s);
 
                idx := MaxInt;
-               for i := 0 to 8 do
+               for i := 0 to 9 do
                        if wkIdx[i] <> 0 then idx := Min(wkIdx[i], idx);
 
                if idx = MaxInt then begin
@@ -1392,10 +1418,14 @@ begin
                        Break;
                end;
 
-               if (idx > 1) and (Copy(s, idx - 1, 1) = '"') then begin
+               if (idx > 1) and
+                       (Pos( ANCHOR_REF, Copy(s, idx - anchorLen, anchorLen ) ) > 0) then begin
                        //\8aù\82É\83\8a\83\93\83N\83^\83O\82ª\82Â\82¢\82Ä\82¢\82é\82Á\82Û\82¢\82Æ\82«\82Í\83\80\83V
-                       Result := Result + Copy(s, 0, idx + Length('http://') - 1);
-                       s := Copy(s, idx + Length('http://'), length(s));
+                       href := Copy( s, idx, Length( s ) );
+                       Result := Result + Copy( s, 1, idx + Pos( '</a>', href ) + Length( '</a>' ) - 2 );
+                       s := href;
+                       s := Copy( s, Pos( '</a>', s ) + Length( '</a>' ), Length( s ) );
+
                        Continue;
                end;
 
@@ -1466,7 +1496,7 @@ begin
                        ws := Copy(ws, i + 1, Length(ws) - i - 1);
                        if IsNumeric(ws) then
                                Result.FCount := StrToInt(ws);
-                       Result.FTitle := Trim(StringReplace(Result.FTitle, LeftK + ws + RightK, '', [rfReplaceAll]));
+                       Result.FTitle := Trim(CustomStringReplace(Result.FTitle, LeftK + ws + RightK, ''));
                        break;
                end;
        end;
@@ -1660,22 +1690,22 @@ function TGikoSys.ReadThreadFile(FileName: string; Line: Integer): string;
 var
        fileTmp : TStringList;
 begin
-  Result := '';
-  if FileExists(FileName) then begin
-    fileTmp := TStringList.Create;
-    try
-      try
-        fileTmp.LoadFromFile( FileName );
-        if ( Line  >= 1 ) and ( Line  < fileTmp.Count + 1 ) then begin
-               Result := fileTmp.Strings[ Line-1 ];
-        end;
-      except
-             //on EFOpenError do Result := '';
-      end;
-    finally
-           fileTmp.Free;
-    end;
-  end;
+       Result := '';
+       if FileExists(FileName) then begin
+               fileTmp := TStringList.Create;
+               try
+                       try
+                               fileTmp.LoadFromFile( FileName );
+                               if ( Line       >= 1 ) and ( Line       < fileTmp.Count + 1 ) then begin
+                                       Result := fileTmp.Strings[ Line-1 ];
+                               end;
+                       except
+                               //on EFOpenError do Result := '';
+                       end;
+               finally
+                       fileTmp.Free;
+               end;
+       end;
 end;
 
 (*************************************************************************
@@ -1794,7 +1824,7 @@ begin
        end else begin
                S := s;
                for i := Low(ERASECHAR) to      High(ERASECHAR) do      begin
-                       S := StringReplace(S, ERASECHAR[i], '', [rfReplaceAll]);
+                       S := CustomStringReplace(S, ERASECHAR[i], '');
                end;
                if (Length(S) <= ALength) then begin
                        Result := S;
@@ -2326,6 +2356,8 @@ var
 //     Len: Integer;
 begin
        Result := False;
+       if RightStr( Host, 1 ) = '/' then
+               Host := Copy( Host, 1, Length( Host ) - 1 );
        OutputDebugString(pchar(HOST_NAME[0]));
        for i := 0 to Length(HOST_NAME) - 1 do begin
 //             Len := Length(HOST_NAME[i]);
@@ -2337,10 +2369,6 @@ begin
 end;
 
 function TGikoSys.Parse2chURL(const url: string; const path: string; const document: string; var BBSID: string; var BBSKey: string): Boolean;
-const
-       READ_PATH: string =                     '/test/read.cgi/';
-       OLD_READ_PATH: string =         '/test/read.cgi?';
-       KAKO_PATH: string =                     '/kako/';
 var
        Index: Integer;
        s: string;
@@ -2657,6 +2685,285 @@ begin
        end;
 end;
 
+function       TGikoSys.GetThreadURL2BoardURL(
+       inURL : string
+) : string;
+var
+       threadItem      : TThreadItem;
+       boardPlugIn     : TBoardPlugIn;
+       i                                               : Integer;
+begin
+
+       //===== \83v\83\89\83O\83C\83\93
+       try
+               for i := Length( BoardPlugIns ) - 1 downto 0 do begin
+                       if Assigned( Pointer( BoardPlugIns[ i ].Module ) ) then begin
+                               if BoardPlugIns[ i ].AcceptURL( inURL ) = atThread then begin
+                                       boardPlugIn := BoardPlugIns[ i ];
+                                       threadItem      := TThreadItem.Create( boardPlugIn, inURL );
+                                       Result                  := BoardPlugIns[ i ].GetBoardURL( Longword( threadItem ) );
+                                       threadItem.Free;
+
+                                       Break;
+                               end;
+                       end;
+               end;
+       except
+               // exception \82ª\94­\90\82µ\82½\8fê\8d\87\82Í\93à\95\94\8f\88\97\9d\82É\94C\82¹\82½\82¢\82Ì\82Å\82±\82±\82Å\82Í\89½\82à\82µ\82È\82¢
+       end;
+
+       if Length( Result ) = 0 then
+               Result := GikoSys.Get2chThreadURL2BoardURL( inURL );
+
+end;
+
+function       TGikoSys.Get2chThreadURL2BoardURL(
+       inURL : string
+) : string;
+var
+       Protocol, Host, Path, Document, Port, Bookmark : string;
+       BBSID, BBSKey : string;
+begin
+
+       ParseURI( inURL, Protocol, Host, Path, Document, Port, Bookmark );
+       Parse2chURL( inURL, Path, Document, BBSID, BBSKey );
+
+       Result := Copy( inURL, 1, Pos( '/test/read.cgi', inURL ) ) + BBSID + '/';
+
+end;
+
+function       TGikoSys.Get2chBrowsableThreadURL(
+       inURL                   : string
+) : string;
+var
+       Protocol, Host, Path, Document, Port, Bookmark : string;
+       BBSID, BBSKey : string;
+       foundPos        : Integer;
+begin
+
+       if Pos( KAKO_PATH, inURL ) > 0 then begin
+               Result := inURL;
+       end else begin
+               ParseURI( inURL, Protocol, Host, Path, Document, Port, Bookmark );
+               Parse2chURL( inURL, Path, Document, BBSID, BBSKey );
+               foundPos := Pos( '/test/read.cgi', inURL ) - 1;
+
+               if Is2chHost( Host ) then begin
+                       Result := Copy( inURL, 1, foundPos ) +
+                               READ_PATH + BBSID + '/' + BBSKey + '/l50';
+               end else begin
+                       Result := Copy( inURL, 1, foundPos ) +
+                               OLD_READ_PATH + 'bbs=' + BBSID + '&key=' + BBSKey + '&ls=50';
+               end;
+       end;
+
+end;
+
+function       TGikoSys.Get2chBoard2ThreadURL(
+       inBoard : TBoard;
+       inKey           : string
+) : string;
+var
+       server  : string;
+begin
+
+       server := UrlToServer( inBoard.URL );
+       if Is2chHost( server ) then
+               Result := server + 'test/read.cgi/' + inBoard.BBSID + '/' + inKey + '/l50'
+       else
+               Result := server + 'test/read.cgi?bbs=' + inBoard.BBSID + '&key=' + inKey + '&ls=50';
+
+end;
+
+(*************************************************************************
+ *\8b@\94\\96¼\81@\81@\81F\83{\81[\83h\83t\83@\83C\83\8b\93Ç\82Ý\8d\9e\82Ý
+ *\89Â\8e\8b\81@\81@\81@\81FPublic
+ *\97\9a\97ð\82P\81@\81@\81F\90V\8bK\8dì\90¬
+ *************************************************************************)
+procedure TGikoSys.ReadBoardFile;
+var
+       idx: Integer;
+       ini: TMemIniFile;
+       CategoryList: TStringList;
+       BoardList: TStringList;
+       Category: TCategory;
+       Board: TBoard;
+       inistr: string;
+       RoundItem: TRoundItem;
+
+       // BBS \94z\97ñ(1 \83{\81[\83h\83t\83@\83C\83\8b\82É\82Â\82« 1 \8d\80\96Ú)
+       bbsList                         : array of TStringList;
+       boardFileList   : TStringList;
+
+       i, iBound                       : Integer;
+       j, jBound                       : Integer;
+       k, kBound                       : Integer;
+       l, lBound                       : Integer;
+begin
+       ini := TMemIniFile.Create('');
+       try
+               // BBS \82Ì\8aJ\95ú
+               try
+                       for i := 0 to Length( BBSs ) - 1 do
+                               BBSs[ i ].Free;
+               except
+               end;
+               SetLength( BBSs, 0 );
+
+               // \94Â\83\8a\83X\83g\82Ì\97ñ\8b\93
+               if FileExists( GikoSys.GetBoardFileName ) then begin
+                       l                                                               := Length( bbsList );
+                       SetLength( bbsList, l + 1 );
+                       bbsList[ l ]            := TStringList.Create;
+                       bbsList[ l ].LoadFromFile( GikoSys.GetBoardFileName );
+
+                       SetLength( BBSs, l + 1 );
+                       BBSs[ l ]                               := TBBS.Create( GikoSys.Setting.LogFolder );
+                       BBSs[ l ].Title := '\82Q\82¿\82á\82ñ\82Ë\82é';
+               end;
+
+               if FileExists( GikoSys.GetCustomBoardFileName ) then begin
+                       l                                                               := Length( bbsList );
+                       SetLength( bbsList, l + 1 );
+                       bbsList[ l ]            := TStringList.Create;
+                       bbsList[ l ].LoadFromFile( GikoSys.GetCustomBoardFileName );
+
+                       SetLength( BBSs, l + 1 );
+                       BBSs[ l ]                               := TBBS.Create( GikoSys.Setting.LogFolder );
+                       BBSs[ l ].Title := '\82»\82Ì\91¼';
+               end;
+
+               // Board \83t\83H\83\8b\83_
+               if DirectoryExists( GikoSys.Setting.GetBoardDir ) then begin
+                       BoardFileList := TStringList.Create;
+                       try
+                               GikoSys.GetFileList( GikoSys.Setting.GetBoardDir, '*', BoardFileList, True, True );
+                               l := Length( bbsList );
+                               for i := BoardFileList.Count - 1 downto 0 do begin
+                                       SetLength( bbsList, l + 1 );
+                                       bbsList[ l ]            := TStringList.Create;
+                                       bbsList[ l ].LoadFromFile( BoardFileList[ i ] );
+
+                                       SetLength( BBSs, l + 1 );
+                                       BBSs[ l ]                               := TBBS.Create( GikoSys.Setting.LogFolder );
+                                       BBSs[ l ].Title := ChangeFileExt( ExtractFileName( BoardFileList[ i ] ), '' );
+
+                                       Inc( l );
+                               end;
+                       finally
+                               BoardFileList.Free;
+                       end;
+               end;
+
+               lBound := Length( bbsList ) - 1;
+               for l := 0 to lBound do begin
+                       ini.SetStrings( bbsList[ l ] );
+                       CategoryList    := TStringList.Create;
+                       BoardList                       := TStringList.Create;
+                       try
+                               ini.ReadSections( CategoryList );
+
+                               iBound := CategoryList.Count - 1;
+                               for i := 0 to iBound do begin
+                                       ini.ReadSection( CategoryList[i], BoardList );
+                                       Category                                := TCategory.Create;
+                                       Category.No                     := i + 1;
+                                       Category.Title  := CategoryList[i];
+
+                                       jBound := BoardList.Count - 1;
+                                       for j := 0 to jBound do begin
+                                               Board := nil;
+                                               inistr := ini.ReadString(CategoryList[i], BoardList[j], '');
+
+                                               //===== \83v\83\89\83O\83C\83\93
+                                               try
+                                                       kBound := Length( BoardPlugIns ) - 1;
+                                                       for k := 0 to kBound do begin
+                                                               if Assigned( Pointer( BoardPlugIns[ k ].Module ) ) then begin
+                                                                       if BoardPlugIns[ k ].AcceptURL( inistr ) = atBoard then begin
+                                                                               Board := TBoard.Create( BoardPlugIns[ k ], inistr );
+
+                                                                               Break;
+                                                                       end;
+                                                               end;
+                                                       end;
+                                               except
+                                                       // exception \82ª\94­\90\82µ\82½\8fê\8d\87\82Í\93à\95\94\8f\88\97\9d\82É\94C\82¹\82½\82¢\82Ì\82Å\82±\82±\82Å\82Í\89½\82à\82µ\82È\82¢
+                                               end;
+
+                                               if Board = nil then
+                                                       Board := TBoard.Create( nil, inistr );
+                                               Board.BeginUpdate;
+                                               Board.No := j + 1;
+                                               Board.Title := BoardList[j];
+                                               Board.RoundDate := ZERO_DATE;
+
+                                               idx := RoundList.Find(Board);
+                                               if idx <> -1 then begin
+                                                       RoundItem                               := RoundList.Items[idx, grtBoard];
+                                                       Board.Round                     := True;
+                                                       Board.RoundName := RoundItem.RoundName;
+                                               end;
+                                               Category.Add(Board);
+                                               Board.LoadSettings;
+                                               Board.EndUpdate;
+                                       end;
+
+                                       BBSs[ l ].Add( Category );
+                               end;
+                       finally
+                               BoardList.Free;
+                               CategoryList.Free;
+                       end;
+               end;
+       finally
+               ini.Free;
+               lBound := Length( bbsList ) - 1;
+               for l := 0 to lBound do
+                       bbsList[ l ].Free;
+       end;
+end;
+
+function       TGikoSys.GetUnknownCategory : TCategory;
+const
+       UNKNOWN_CATEGORY = '(\96¼\8fÌ\95s\96¾)';
+begin
+
+       if Length( BBSs ) < 2 then begin
+               Result := nil;
+               Exit;
+       end;
+
+       Result := BBSs[ 1 ].FindCategoryFromTitle( UNKNOWN_CATEGORY );
+       if Result = nil then begin
+               Result                          := TCategory.Create;
+               Result.Title    := UNKNOWN_CATEGORY;
+               BBSs[ 1 ].Add( Result );
+       end;
+
+end;
+
+function       TGikoSys.GetUnknownBoard( inPlugIn : TBoardPlugIn; inURL : string ) : TBoard;
+var
+       category : TCategory;
+const
+       UNKNOWN_BOARD = '(\96¼\8fÌ\95s\96¾)';
+begin
+
+       category := GetUnknownCategory;
+       if category = nil then begin
+               Result := nil;
+       end else begin
+               Result := category.FindBoardFromTitle( UNKNOWN_BOARD );
+               if Result = nil then begin
+                       Result                          := TBoard.Create( inPlugIn, inURL );
+                       Result.Title    := UNKNOWN_BOARD;
+                       category.Add( Result );
+               end;
+       end;
+
+end;
+
 initialization
        GikoSys := TGikoSys.Create;