<%@ Language=VBScript %> <% Option Explicit %> <% ' ASP error 404 handler ' ===================== ' http://walter.bislins.ch/doku/error404 ' ' Note: local virtual webs have urls like http://localhost/<virtualweb>/... ' To resolve local urls, you must specify VIRTUALWEB = "<virtualweb>" const REDIRECT_FOLDER = "../../wiki_secure/pages/work" const REDIRECT_FILE = "Spezial: Redirect Data" const REDIRECT_EDITOR = "work/index.asp?page=Spezial%3A+Redirect+Data" const CUSTOM_HANDLER = "index.asp?page=Fehler%3A+Seite+nicht+gefunden" const VIRTUALWEB = "walti" const PASS_URL_BY_PAR = false dim EXCLUDE_LIST EXCLUDE_LIST = Array( "/apple-touch-icon.*\.png" ) ' include = pattern.inc ' ===================== dim GPattern_RegExp, GPattern_LastPattern, GPattern_LastIC set GPattern_RegExp = new RegExp GPattern_RegExp.Global = true GPattern_RegExp.Multiline = true GPattern_LastPattern = "" GPattern_LastIC = false dim GPattern_RegExpT, GPattern_LastPatternT, GPattern_LastICT set GPattern_RegExpT = new RegExp GPattern_RegExpT.Global = false GPattern_RegExpT.Multiline = true GPattern_LastPatternT = "" GPattern_LastICT = false dim GPattern_ExtractResult GPattern_ExtractResult = Array() function TestPattern( aString, aPattern, bIgnoreCase ) if (GPattern_LastPatternT <> aPattern) or (GPattern_LastICT <> bIgnoreCase) then GPattern_RegExpT.IgnoreCase = bIgnoreCase GPattern_RegExpT.Pattern = aPattern GPattern_LastPatternT = aPattern GPattern_LastICT = bIgnoreCase end if TestPattern = GPattern_RegExpT.Test(aString) end function function ExtractPattern( aStr, aPattern ) dim x if (GPattern_LastPattern <> aPattern) or (GPattern_LastIC <> true) then GPattern_RegExp.IgnoreCase = true GPattern_RegExp.Pattern = aPattern GPattern_LastPattern = aPattern GPattern_LastIC = true end if GPattern_ExtractResult = Array() x = GPattern_RegExp.Replace( aStr, GetRef("GPattern_ExtractPattern") ) ExtractPattern = GPattern_ExtractResult end function function GPattern_ExtractPattern(match,p1,pos,source) dim last if IsEmpty(p1) then p1 = "" last = UBound(GPattern_ExtractResult) + 1 redim preserve GPattern_ExtractResult(last) GPattern_ExtractResult(last) = p1 GPattern_ExtractPattern = match end function ' include = seq.inc ' ================= class CParser private mString private mPos private sub Class_Initialize() mString = "" mPos = 1 end sub sub Init( aString ) mString = aString mPos = 1 end sub sub SkipSubData( ) ' skips a whole sub data struct dim x x = GetStrD("") end sub function GetNumD( aDefault ) dim p, s GetNumD = aDefault p = InStr(mPos,mString,"|") if p > 0 then s = Mid(mString,mPos,p-mPos) if IsNumeric(s) then GetNumD = CLng(s) mPos = p + 1 end if end if end function function GetStrD( aDefaultStr ) dim n GetStrD = aDefaultStr n = GetNumD( -1 ) if n = 0 then GetStrD = "" elseif n > 0 and mPos+n <= Len(mString) then GetStrD = Mid(mString,mPos,n) mPos = mPos + n + 1 end if end function function GetFixStr( aStrLen ) GetFixStr = "" if mPos+aStrLen-1 <= Len(mString) then GetFixStr = Mid(mString,mPos,aStrLen) mPos = mPos + aStrLen end if end function end class function NewParser( aString ) set NewParser = new CParser NewParser.Init aString end function ' include = fs.inc ' ================ class CFileSystem public FileFormat ' 0 -> ASCII (default), -1 -> Unicode, -2 -> System Settings private mFSO private sub Class_Initialize() FileFormat = 0 set mFSO = Server.CreateObject("Scripting.FileSystemObject") end sub function MapPath( aPath ) ' returns the absolute path to aPath if aPath = "" then MapPath = Server.MapPath(".") elseif InStr(aPath,":\") then ' path is absolute already MapPath = aPath else MapPath = Server.MapPath(aPath) end if end function function AppendSlash( aPath ) AppendSlash = aPath if InStrRev(AppendSlash,"/") <> Len(AppendSlash) then AppendSlash = AppendSlash & "/" end if end function function AppendName( aPath, aName ) AppendName = AppendSlash(aPath) & aName end function function FileExists( aPath ) dim absPath on error resume next absPath = MapPath(aPath) FileExists = mFSO.FileExists(absPath) if Err <> 0 then FileExists = false Err.Clear end if end function function FolderExists( aPath ) dim absPath absPath = MapPath(aPath) on error resume next FolderExists = mFSO.FolderExists(absPath) if Err <> 0 then FolderExists = false Err.Clear end if end function function ReadFile( aPath ) ' require FileExists(aPath) dim file, tstream set file = mFSO.GetFile(MapPath(aPath)) set tstream = file.OpenAsTextStream(1, FileFormat) if tstream.AtEndOfStream then ReadFile = "" else ReadFile = tstream.Readall end if end function end class dim FS set FS = new CFileSystem ' include = cvsread.inc ' ===================== ' Single function to read latest content of a CVS file: CvsRead ' ' Uses: ' - fs.inc : FS.AppendName, FS.FileExists, FS.ReadFile ' - seq.inc : CParser const CvsMagic = "CVS|" function CvsRead( aRepository, aFilename ) ' aRepository must be a valid file system folder name (e.g. "../folder") ' aFilename as raw page name ' returns latest content as string dim rawData, p, path CvsRead = "" path = FS.AppendName( aRepository, Server.UrlEncode(aFilename) ) if not FS.FileExists(path) then exit function rawData = FS.ReadFile(path) set p = NewParser( rawData ) if (p.GetFixStr(Len(CvsMagic)) = CvsMagic) then ' skip Header p.SkipSubData ' skip VersionData p.SkipSubData CvsRead = p.GetStrD("") else CvsRead = rawData end if end function ' Main ' ==================================== class CRedir public FromList public ToList public LocalUrl public State private sub Class_Initialize() FromList = Array() ToList = Array() LocalUrl = "" State = "Empty Redirection List" InitRelPath end sub private sub ArrayAdd( ByRef aArray, aStr ) dim pos pos = UBound(aArray) + 1 redim preserve aArray(pos) aArray(pos) = aStr end sub function Load() dim page, nl, path, pat, list, i, entry, src, dest Load = false nl = vbCRLF ' first try to read from CVS repository then from file system State = "Reading fom CVS" page = CvsRead( REDIRECT_FOLDER, REDIRECT_FILE ) if page = "" then State = "Searching data file" path = FS.AppendName( REDIRECT_FOLDER, REDIRECT_FILE ) if not FS.FileExists( path ) then exit function State = "Reading data file" page = FS.ReadFile( path ) end if if page = "" then exit function State = "Extracting redirects" ' try to extract data from wiki page or use all data pat = ExtractPattern( page, "{{data}}"&nl&"([^\0]*)"&nl&"{{end data}}" ) if UBound(pat) >= 0 then page = Join( pat, vbCRLF&vbCRLF ) pat = ExtractPattern( page, "<code>"&nl&"([^\0]*)"&nl&"</code>" ) if UBound(pat) >= 0 then page = Join( pat, vbCRLF&vbCRLF ) ' reformat lines page = Replace( page, nl & "->", " ->" ) list = Split( page, nl ) if UBound(list) < 0 then exit function for i = 0 to UBound(list) entry = Trim(list(i)) if InStr(entry," -> ") then pat = Split(entry," -> ") src = Trim(pat(0)) dest = Trim(pat(1)) if InStrRev(src,"/") = Len(src) then src = Left(src,Len(src)-1) ArrayAdd FromList, src ArrayAdd ToList, dest Load = true end if next if Load then State = "" end function function FindRedirect(aPath) dim i for i = 0 to UBound(FromList) if LCase(FromList(i)) = LCase(aPath) then FindRedirect = i exit function end if next FindRedirect = -1 end function sub InitRelPath() dim p, host, path host = Request.ServerVariables("HTTP_HOST") ' handle local webs if (host = "localhost") or (host = "127.0.0.1") and VIRTUALWEB <> "" then path = Request.ServerVariables("PATH_INFO") if InStr(1,path,VIRTUALWEB,1) <= 2 then LocalUrl = VIRTUALWEB end if end if end sub function GetParams( aUrl ) ' returns from http://www.aphs.ch/d/dir/file.ext?params -> params dim p GetParams = "" ' remove parameters p = InStrRev(aUrl,"?") if p > 0 then GetParams = Right(aUrl,Len(aUrl)-p) end function function GetRelPath( aUrl ) ' returns from http://www.aphs.ch/d/dir/file.ext?params -> d/dir/file.ext ' returns from http://localhost/<virtualweb>/d/dir/file.ext?params -> d/dir/file.ext dim p, path path = aUrl ' remove parameters p = InStrRev(path,"?") if p > 0 then path = Left(path,p-1) ' remove protocol and host p = InStr(path,"//") if p > 0 then path = Right(path,Len(path)-p-1) p = InStr(path,"/") if p > 0 then path = Right(path,Len(path)-p) else if InStr(path,"/") = 1 then path = Right(path,Len(path)-1) end if ' remove virtual web id if VIRTUALWEB <> "" then if InStr(1,path,VIRTUALWEB,1) = 1 then path = Right(path,Len(path)-Len(VIRTUALWEB)-1) end if end if GetRelPath = path end function function CutName( aPath ) dim p p = InStrRev(aPath,"/") if p > 0 then CutName = Left(aPath,p-1) else CutName = aPath end if end function sub DoRedirect(aPath, aPars) dim path path = CompleteToPath(aPath) ' append aPars if last char of aPath is "?" if Mid(path,Len(path),1) = "?" then if aPars <> "" then path = path & aPars else ' remove "?" from path path = Left(path,Len(path)-1) end if end if Response.Clear Response.Status = "301 Moved Permanently" Response.AddHeader "Location", path Response.End end sub function CompleteToPath( aPath ) ' completes aPath to an absolute path taking into account virtual urls dim path path = aPath if InStr(path,"/") <> 1 then path = "/" & path if LocalUrl <> "" then path = "/" & LocalUrl & path CompleteToPath = path end function function IsUrlInExcludeList( aUrl ) dim i, excl IsUrlInExcludeList = true for i = 0 to UBound(EXCLUDE_LIST) excl = EXCLUDE_LIST(i) if TestPattern( aUrl, excl, true ) then exit function next IsUrlInExcludeList = false end function sub RedirectTo( aUrl ) ' aUrl: original url that failed (e.g. http://host/dir/file.ext?params) ' Note: aUrl is passed to other pages by a session var named "FailedUrl" ' a localized version of aUrl is passed by session var "FaildPath" (e.g. dir/file.exp) dim relPath, oldPath, pars, i, saveRelPath if IsUrlInExcludeList(aUrl) then exit sub pars = GetParams( aUrl ) relPath = GetRelPath( aUrl ) saveRelPath = relPath Session("FailedUrl") = "" Session("FailedPath") = "" ' try to find a valid redirect by cutting parts of url step by step if relPath <> "" then i = FindRedirect(relPath) if i >= 0 then DoRedirect ToList(i), pars do oldPath = relPath relPath = CutName(relPath) if oldPath <> relPath then i = FindRedirect(relPath) if i >= 0 then DoRedirect ToList(i), pars end if loop until oldPath = relPath end if ' call custom handler Session("FailedUrl") = aUrl Session("FailedPath") = saveRelPath if CUSTOM_HANDLER <> "" then if DestinationExists( CUSTOM_HANDLER ) then relPath = CompleteToPath(CUSTOM_HANDLER) if PASS_URL_BY_PAR then relPath = relPath & "?url=" & Server.UrlEncode(aUrl) Response.Redirect relPath Response.End end if end if ' fallback to simple error page of this asp file end sub function DestinationExists( aPath ) dim p, path path = aPath p = InStrRev(path,"?") if p > 0 then path = Left(path,p-1) path = CompleteToPath( path ) DestinationExists = FS.FileExists(path) if not DestinationExists then DestinationExists = FS.FolderExists(path) end function function Check() dim path, i for i = 0 to UBound(ToList) path = ToList(i) if not DestinationExists(path) then Check = "Error: Nonexistent redirect destination " & (i+1) & ": " & ToList(i) exit function end if next if CUSTOM_HANDLER <> "" then if not DestinationExists( CUSTOM_HANDLER ) then Check = "Error: Nonexistent CUSTOM_HANDLER: " & CUSTOM_HANDLER exit function end if end if Check = "All OK, Redirect-List-Size = " & UBound(FromList)+1 end function end class function Redirect() dim redir, path, p Redirect = "" path = Request.QueryString ' remove 404; at start inserted from webserver p = InStr(path,";") if p > 0 and p <= 4 then path = Right(path,Len(path)-p) set redir = new CRedir if not redir.Load() then if path = "" then Redirect = "Loading redirect list failed: " & redir.State exit function end if ' Testmode if path = "" then Redirect = redir.Check() exit function end if redir.RedirectTo path end function dim msg msg = Redirect() ' Fallback error 404 page ' ======================= %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <html> <head> <title>Fehler: Seite nicht gefunden</title> </head> <body> <% if msg = "" then %> <h1>Fehler: Seite nicht gefunden</h1> <% else %> <h1>Test of Error 404 Redirect Page</h1> <% end if %> <p> <% if msg <> "" then Response.Write "<h2>State of Selftest</h2>" & vbCRLF Response.Write "<p>" & msg & "</p>" & vbCRLF if REDIRECT_EDITOR <> "" then Response.Write "<p><a href=""" & REDIRECT_EDITOR & """>Edit Redirect Datas</a></p>" & vbCRLF end if else %> Sie sind über einen ungültigen Link auf diese Seite gestossen. <br> Leider konnte keine Weiterleitung auf eine passende Seite gefunden werden. <br> Überprüfen Sie die Addresse im Browser oder besuchen Sie die Homepage:</p> <p><a href="/">Zur Homepage</a> <% end if %> </p> </body> </html>