asp fileExists always returning false - asp-classic

Trying to use a loop to check if images exists however it is always returning false. I am sure I am doing something simple and stupid but here is the code:
dim fs, sql_except
set fs=Server.CreateObject("Scripting.FileSystemObject")
if Not rs.eof then
arrRS = rs.GetRows(30,0)
set rs = nothing
If IsArray(arrRS) Then
For i = LBound(arrRS, 2) to UBound(arrRS, 2)
sku = arrRS(0, i)
if (fs.FileExists("../i/"&sku&".gif")=false) Then
response.write sku&"does not exist<br>"
end if
next
end if
erase arrRS
end if
set fs=nothing

You appear to be operating under the impression that the current folder context the your call to FileExists will assume is the physical folder containing the ASP script being executed. This is not so, it most likely will be "C:\windows\system32\inetsrv". You are also using URL path element separator / where FileExists is expecting windows physical path folder separator \.
You need to use Server.MapPath to resolve the path. This may work:
if Not fs.FileExists(Server.MapPath("../i/"&sku&".gif")) then
However you may run in to trouble with the parent path "..", this may not be allowed for security reasons. This might be a better approach:
Dim path : path = Server.MapPath("/parentFolder/i") & "\"
For i = LBound(arrRS, 2) to UBound(arrRS, 2)
sku = arrRS(0, i)
if Not fs.FileExists(path & sku & ".gif") Then
response.write Server.HTMLEncode(sku) & " does not exist<br>"
end if
next
Where "parentFolder" is the absolute path from the site root.

Related

System.Web.HttpException: The SaveAs method is configured to require a rooted path, and the path is not rooted

The error that I'm facing is :
System.Web.HttpException: The SaveAs method is configured to require a rooted path, and the path '~\FileUpload\2019\HS-123.png' is not rooted.
which I find is a bit weird since my code here:
Dim folderPath1 As String = ("~\FileUpload\" & Today.Year.ToString & "\")
If fileuploadSituation.HasFile Then
Dim extension As String = (Path.GetExtension(fileuploadSituation.FileName))
fileuploadSituation.SaveAs(Server.MapPath(folderPath1) & ID + extension)
End If
this code here work fine. But not this code:
If fileuploadSuggest.HasFile Then
Dim extension1 As String = (Path.GetExtension(fileuploadSuggest.FileName))
fileuploadSuggest.SaveAs(Server.MapPath(folderPath1) & ID + extension1)
End If
It work for the fileuploadSituation but not fileUploadSuggest even though they use the same path. Is there anything suggestion that I could use to apply?

Searching C Drive for a file with VBScript

I am very new to VBScript, and I am trying to write a simple script that will extract a file in a directory to a new directory. So far this is what I have (and it works well):
'USER VAR REPRESENTS WINDOWS USERNAME
Set oShell = CreateObject( "WScript.Shell" )
user=oShell.ExpandEnvironmentStrings("%UserName%")
'FOLDER TO BE EXTRACTED
ZipFile="C:\Users\"&user&"\Downloads\Test.zip"
'LOCATION TO EXTRACT FILES
ExtractTo="C:\Users\"&user&"\desktop"
'EXTRACT ZIP FILE
Set objShell = CreateObject("Shell.Application")
Set FilesInZip=objShell.NameSpace(ZipFile).items
objShell.NameSpace(ExtractTo).CopyHere(FilesInZip)
Set fso = Nothing
Set objShell = Nothing
Set oShell = Nothing
Now, if possible, if the "Desktop" folder cannot be found, or the "Test.zip" file cannot be found, I would like to search the C Drive for them, and then proceed with extracting, etc. I have seen some examples, but I cannot understand how to replicate them. How can I search the entire C drive and sub folders for these files?
Help would be appreciated, thanks in advance!
In general a recursive search can be done like this:
Function SearchFolder(fldr, name)
Set SearchFolder = Nothing
For Each f In fldr.Files
If LCase(f.Name) = LCase(name) Then
Set SearchFolder = f
Exit Function
End If
Next
For Each sf In fldr.SubFolders
Set result = SearchFolder(sf, name)
If Not result Is Nothing Then
Set SearchFolder = result
Exit Function
End If
Next
End Function
Set fso = CreateObject("Scripting.FileSystemObject")
Set f = SearchFolder(fso.GetFolder("C:\"), "Test.zip")
However, searching a whole drive that way will take quite some time. Also there are several folders that users don't have access to, so you'll have to account for that if you want to implement a search like this.

VBS: FileSystemObject use in Recursion

Currently I'm working on a script that will go through a given folder and search for all files with a specific extension. It will then print out the name and sum the file size. I believe i have most of the issues sorted out, so this question isn't about how to do this.
Instead, I want to know what would be the best practice for using FileSystemObject streams in a recursive function. Should I be using a single stream for all calls (either global, or passed), or should I be creating a new stream for each recursive step?
For extra fun, I'm planning on having this access multiple PCs, and over UNC path. And yes, I expect there's a better way of doing this, but I'm relatively new with VBS.
Current code:
'Recursive Function handles search of files in folder and subfolders.
Function UNCSearchFolder(strUNCRootPath, strUNCNextFolder)
Dim objFSOUNCSearchFolder, objFSOUNCSearchFolder2, colFolderList, objFolder, strCurrentFolder, strSubFolder
'Get list of Subfolders in folder: <Rootpath>\<Nextfolder>
strCurrentFolder = strUNCRootPath & "\" & strUNCNextFolder & "\"
Set objFSOUNCSearchFolder = CreateObject("Scripting.FileSystemObject")
Set objFSOUNCSearchFolder2 = objFSOUNCSearchFolder.GetFolder(strCurrentFolder)
Set colFolderList = objFSOUNCSearchFolder2.SubFolders
'Subfolder dive
For Each objFolder in colFolderList
strSubFolder = objFolder.name
'REMOVE THIS ECHO LATER
wscript.echo strSubFolder
UNCSearchFolder(strCurrentFolder, strSubFolder)
Next
'Search for files here
'GC on File Streams
Set objFSOUNCSearchFolder2 = Nothing
Set objFSOUNCSearchFolder = Nothing
End Function
So, should one filestream be used for all accesses or should each step use one separately? Is it a moot point? Will this cause multiple connections to each system or should it only use one? Basically I want the script to work without disrupting users, or causing weird responses (ie, running out of active connections). The script will only be used a couple times for an audit we're doing, but may eventually be repurposed for future audits.
Let me know what you think. Thanks for any help,
If you choose to set a reference to FSO inside your function,
then in each recursion will be used new FSO object.
Using single FSO object (either global, or passed) is quite enough.
At least I don't know any benefit of using multiple FSO instances.
[EDIT] I appreciate #AnsgarWiechers comment, and to make the code ready for re-using, while kept the FSO out of the function, we can wrap our function in a class.
With New FileInfo
WScript.Echo .FileSize("C:\temp", "txt", True)
End With
Class FileInfo
Private m_oFSO
Public Function FileSize(sRootDir, sExtension, bRecursive)
Dim oFolder, oFile, sFExt
sFExt = LCase(sExtension)
Set oFolder = m_oFSO.GetFolder(sRootDir)
For Each oFile In oFolder.Files
If LCase(m_oFSO.GetExtensionName(oFile.Name)) = sFExt Then
FileSize = FileSize + oFile.Size
End If
Next
If bRecursive Then
Dim oSubFolder
For Each oSubFolder In oFolder.SubFolders
FileSize = FileSize + FileSize(oSubFolder, sExtension, True)
Next
End If
End Function
Private Sub Class_Initialize
Set m_oFSO = CreateObject("Scripting.FileSystemObject")
End Sub
Private Sub Class_Terminate
Set m_oFSO = Nothing
End Sub
End Class

Listing a folder structure in Classic ASP

I've developed a secure page in ASP for the company I work for. There is a landing (login page) that once you are authenticated you are taken to a page that has links to several sub pages. Each sub page has a folder structure. For example: There is a heading for Meeting Minutes and then underneath and indented are links referencing PDFs that contain the information. There may be 3 or 4 headings with documents linked beneath.
The original version had a PHP script that ran and would sync up the live site on the server from a folder structure that would be mimicked onto the live site. So if I had a folder called Folder1 and sub folders named test1 test2 test3.. the live site would display them accordingly. Since the site is now in ASP and not PHP.. the PHP script no longer works (since PHP doesn't play well with ASP).
I found a snippet online that somewhat works for what i'm trying to achieve (i.e. Folder/Subfolder/File Name structure), however i'm stuck at the moment with how to link the files so they open when clicked. I keep seeing a %25 in the file name. I know %20 is the same as a blank space and since I am dealing with file and folder names that contain spaces, this appears to be my issue. I've tried adding in a %20 but the spaces become "%2520".
If you look at the code below, there is a link towards the bottom that calls "MapURL". I have that link commented out at the moment as I was trying to figure out where the %25 was coming from.
Anyone have any thoughts on how to get the links to work?
Here is the snippet.
dim path
path = "PATH TO THE FOLDER ON THE SERVER"
ListFolderContents(path)
sub ListFolderContents(path)
dim fs, folder, file, item, url
set fs = CreateObject("Scripting.FileSystemObject")
set folder = fs.GetFolder(path)
'Display the target folder and info.
Response.Write("<ul><b>" & folder.Name & "</b>") '- " _
' & folder.Files.Count & " files, ")
'if folder.SubFolders.Count > 0 then
' Response.Write(folder.SubFolders.Count & " directories, ")
'end if
'Response.Write(Round(folder.Size / 1024) & " KB total." _
' & "</ul>" & vbCrLf)
Response.Write("<ul>" & vbCrLf)
'Display a list of sub folders.
for each item in folder.SubFolders
ListFolderContents(item)
next
'Display a list of files.
for each item in folder.Files
'url = MapURL(item.path)
'Response.Write("<li>" & item.Name & " - " _
Response.Write("<li>" & item.Name & " - " _
& item.Name & "</a>" _
& "</li>" & vbCrLf)
next
Response.Write("</ul>" & vbCrLf)
Response.Write("</ul>" & vbCrLf)
end sub
function MapURL(path)
dim rootPath, url
'Convert a physical file path to a URL for hypertext links.
rootPath = Server.MapPath("/")
url = Right(path, Len(path) - Len(rootPath))
MapURL = Replace(url, "\", "/")
end function
There are several things wrong with your code.
First and foremost, you do not encode the values you output at all. This is a big mistake. You are missing URL-encoding for things that go into the HREF attribute, and you miss HTML-encoding for everything else.
Next, you create a new FileSystemObject with every call to the recursive ListFolderContents() function. This is unnecessarily wasteful and will become slow as soon as there are more than a handful of files to be output.
Your recursive function should take a Folder object as the first argument, not a path. This makes things a lot easier.
The HTML structure you output is invalid. <b> cannot legally be a child of <ul>.
I completely rewrote your code to produce more correct output and to be as fast as possible. Crucial to your problem is the PathEncode() function, it transforms a relative path to a properly encoded URL. The other things should be pretty self-explanatory:
ListFolder "P:\ATH\TO\THE\FOLDER\ON\THE\SERVER"
' -- Main Functions ----------------------------------------------------
Sub ListFolder(path)
Dim fs, rootPath
Set fs = CreateObject("Scripting.FileSystemObject")
rootPath = Replace(path, Server.MapPath("/"), "") & "\"
ListFolderContents fs.GetFolder(path), PathEncode(rootPath)
End Sub
' ----------------------------------------------------------------------
Sub ListFolderContents(folder, relativePath)
Dim child
Say "<ul>"
Say "<li><div class=""folder"">" & h(folder.Name) & "</div>"
For Each child In folder.SubFolders
If Not IsHidden(child) Then
ListFolderContents child, relativePath & PathEncode(child.Name) & "/"
End If
Next
relativePath = h(relativePath)
For Each child In folder.Files
If Not IsHidden(child) Then
Say "<li>" & h(child.Name) & "</li>"
End If
Next
Say "</ul>"
End Sub
' -- Helper Functions / Shorthands ---------------------------------------
Sub Say(s)
Response.Write s & vbNewLine
End Sub
Function h(s)
h = Server.HTMLEncode(s)
End Function
Function PathEncode(s)
' this creates a more correct variant of what Server.URLEncode would do
PathEncode = Replace(s, "\", "/")
PathEncode = Server.URLEncode(PathEncode)
PathEncode = Replace(PathEncode, "+", "%20")
PathEncode = Replace(PathEncode, "%2F", "/")
PathEncode = Replace(PathEncode, "%2E", ".")
PathEncode = Replace(PathEncode, "%5F", "_")
End Function
Function IsHidden(File)
IsHidden = File.Attributes And 2 = 2
End Function
Notes
Use the <div class="folder"> to apply CSS styles (i.e. bold etc.) to the folder name.
The function will not output hidden files or directories.
The relativePath argument is used to keep the workload as low as possible - when a folder has 1000 files, it makes no sense to calculate the entire relative path 1000 times. With the help of this parameter, only the part that actually changes is processed.
Having functions like Say() or h() around can save you a lot of typing and it keeps the code more clean, too.
You should read up on URL-encoding (and HTML-encoding as well). Seems like you've never come across these things, which is especially bad if your task is to build a secure site.
you probably need extra quotes at the href (""). The best way is to see the generated source code (from the resulting page) like <a href=""" & replace(...) & """>"
Basically, if you use only one quote it just closes the string, but you are missing the HTML quote needed after href= and the closing one.

vbscript / Classic ASP - Is there any way to get your own file name programmatically?

I was just reviewing some old code and found the following (inside foo.asp):
Const ASP_FILENAME = "foo.asp" ' TODO: Update this to the name of this file (if changed)
The variable is only used for logging errors. (ie. "Error in foo.asp - Could not create xxxxx object.") Is there any way to avoid this?
Thanks!
You could parse Request.ServerVariables("url") to get the filename portion. A google search found this code, to which i don't claim credit, which uses the SCRIPT_NAME server variable which seems to make more sense indeed, also taking any url rewriting in to account that might be in place:
function getFileName(fpath, returnExtension)
tmp = fpath
if instrRev(tmp,"/") > 0 then
tmp = mid(tmp, instrRev(tmp,"/")+1)
end if
if returnExtension = false then
if instrRev(tmp,".") > 0 then
tmp = left(tmp, instrRev(tmp,".")-1)
end if
end if
getFileName = tmp
end function
filename = request.ServerVariables("SCRIPT_NAME")
Const ASP_FILENAME = getFileName(filename, true)
From the now-defunct Aspfaq.com (thanks to Archive.org):
How do I get the name of the current URL / page?
This one is pretty easy, but there are two parts.
To retrieve the name of the current file, you can use any of these:
<%
Response.Write Request.ServerVariables("SCRIPT_NAME") & "<br>"
Response.Write Request.ServerVariables("PATH_INFO") & "<br>"
Response.Write Request.ServerVariables("URL") & "<br>"
%>
To make that path local (for example, to use with FileSystemObject), just apply the server.mappath() method to the result.
To get the entire URL, including the http:// or https:// prefix, you can do this:
<%
prot = "http"
https = lcase(request.ServerVariables("HTTPS"))
if https <> "off" then prot = "https"
domainname = Request.ServerVariables("SERVER_NAME")
filename = Request.ServerVariables("SCRIPT_NAME")
querystring = Request.ServerVariables("QUERY_STRING")
response.write prot & "://" & domainname & filename & "?" & querystring
%>
To get the page name ONLY, use something like this:
<%
scr = Request.ServerVariables("SCRIPT_NAME") & "<br>"
if instr(scr,"/")>0 then
scr = right(scr, len(scr) - instrRev(scr,"/"))
end if
response.write scr
%>
Or, without the IF logic:
<%
scr = Request.ServerVariables("SCRIPT_NAME") & "<br>"
loc = instrRev(scr,"/")
scr = mid(scr, loc+1, len(scr) - loc)
response.write scr
%>
Now. If your file is an #INCLUDE within another file, the above scripts will produce the name of the CALLING file (since the included file is first integrated into the calling script, then the ASP within it is all executed in the context of the 'parent' file). One way you can work around this is to re-populate a current_filename variable before loading each include file, for example:
<%
current_filename = "filetoinclude.asp"
%>
<!--#include file='filetoinclude.asp'-->
(And no, don't try passing current_filename as a variable to the #INCLUDE directive; see Article #2042.)
Then, in filetoinclude.asp:
<%
Response.Write "Current file: " & current_filename
%>
Of course, you could just as easily hard-code the filename inside of each include file. But I suppose that solution would somewhat defeat the purpose of retrieving that information at least somewhat dynamically.
I dont know if server.mappath exists on traditional asp, but if so you could use if to know the page filename.
Not saying that anyone here [insert discrete throat clearing cough here] still uses Classic ASP for maintaining and supporting legacy applications, but I recently had the need to do something similar. Refusing to settle for the "it's impossible with Classic ASP" responses out there, I set out to find a way and came up with the following solution.
This approach basically leverages underlying OS commands to literally determine the current filename (whose result is equivalent to using the __FILE__ magic constant in PHP) regardless of whether it's a file include (*.inc) or the script itself (*.asp).
First, to support some sanitization (you can do this some other more "optimal" way if you wish):
'Regex helpers
Function NewRegex(ByVal pattern, ByVal ignore_case, ByVal global)
Set NewRegex = New RegExp
NewRegex.Pattern = pattern
NewRegex.IgnoreCase = ignore_case
NewRegex.Global = global
End Function
Function RegexMatch(ByVal pattern, ByVal subject)
RegexMatch = RegexMatches(subject, pattern, True, False)
End Function
Function RegexMatches(ByVal subject, ByVal pattern, ByVal ignore_case, ByVal global)
RegexMatches = NewRegex(pattern, ignore_case, global).Test(subject)
End Function
And now for a time of "reflection:"
Function GetCurrentFilename(ByVal uniqueId)
'1. Enforce uniqueId format
If Not RegexMatch("^[0-9a-f]+$", uniqueId) Then
Exit Function
End If
'2. Use findstr to scan "readable" files in current directory for uniqueId
Dim shell, cmd, process, fs, filename
Set shell = Server.CreateObject("WScript.Shell")
'See findstr /? for details on switches used below
'cmd = C:\Windows\system32\cmd.exe /c findstr /P /M /C:"uniqueId" "C:\Inetpub\wwwroot\includes\*"
cmd = shell.ExpandEnvironmentStrings("%COMSPEC%") & " /c findstr /P /M /C:""" & uniqueId & """ """ & Server.MapPath(".") & "\*"""
Set process = shell.Exec(cmd)
'3. Use Scripting.FileSystemObject to return the filename portion of the first result returned
Set fs = Server.CreateObject("Scripting.FileSystemObject")
GetCurrentFilename = fs.GetFileName(process.StdOut.ReadLine())
Set fs = Nothing
Set process = Nothing
Set shell = Nothing
End Function
Then, inside whatever file you want to "inspect" the current filename, simply drop the following line, passing in some unique identifier that should not exist in any other file in the current directory but this one:
'myfile.inc
Response.Write "This is in " & GetCurrentFilename("908ab098c")
The result:
This is in somefile.inc
Oh, and if you're interested in what I needed to use this for, here's what it looks like used in a simple breakpoint function:
Function Breakpoint(ByVal line_no, ByVal uniqueId, ByVal msg)
Dim fn
fn = GetCurrentFilename(uniqueId)
Response.Write "[!] Breakpoint hit at Line " & CLng(line_no) & " in " & fn & ": " & Server.HtmlEncode(msg) & vbNewLine
Response.End
End Function
Such that on Line 20 of my code, if I wanted to add my own breakpoint, it would look like this:
Breakpoint 20, "B0001", "Some debug output here"
Output:
[!] Breakpoint hit at Line 20 in somefile.inc: Some debug output here
Happy coding!

Resources