'-----------------------------------------------------------------------
'  smburl3a.vbs (C) Walter Doekes 2007-2008, license: public domain
'-----------------------------------------------------------------------
'
' Call with "register" argument to add the smb:// url handler.
' Call with "unregister" argument to undo "register".
' Call with any "smb://..." url to open the windows file:// url
' handler.
'
' Why?
' Because Firefox and Opera try to "download" file:// urls
' instead of "opening" them. With this, you can use smb:// urls instead.
' This script creates the smb://-protocol and handles it by passing
' fixed urls on to explorer which _can_ "open" the files.
'
' Obviously it would've been better if we could hijack the
' file://-protocol handler. Any suggestions how to do this will be
' highly appreciated.
'
' Where?
' Use it on any Windows that has WScript/VBS.
'
' See: http://wjd.nu/articles/2004/00/opening_smb_files_using_opera
'
'-----------------------------------------------------------------------
' CHANGES
'-----------------------------------------------------------------------
' 20070426 smburl3:
'   Initial version
'
' 20080907 smburl3a:
'   For a while now, browsers escape spaces (that were illegal in URLs
'   in the first place) in URLs. If you're going to present a user with
'   smb://-file links, you must make sure you urlencode all spaces and
'   percent-signs. This version replaces all (non-reserved) encoded
'   characters with their decoded equivalents. Any smb://-links that
'   you provide must have at least space and percent-signs urlencoded!
'   
'-----------------------------------------------------------------------
' FUNCTIONS
'-----------------------------------------------------------------------
'

option explicit

' Returns a numeric value (0..15) or null for hex-char c (0..f)
function chr0x(c)
        chr0x = null
        c = asc(ucase(c))
        if c >= 48 and c <= 57 then chr0x = c - 48
        if c >= 65 and c <= 70 then chr0x = c - 55
end function

' Reallows disallowed characters in URIs and normalizes encoded legal characters.
function urlNormalize(url)
        dim rfc3986reserved, i, cnum
        rfc3986reserved = "!*'();:@&=+$,/?#[]" ' "%" (percent) is removed from this list
        for i = 1 to len(url) - 2
                if mid(url, i, 1) = "%" then
                        cnum = chr0x(mid(url, i + 1, 1)) * 16 + chr0x(mid(url, i + 2, 1))
                        if not isnull(cnum) then
                                cnum = chr(cnum)
                                if instr(rfc3986reserved, cnum) = 0 then
                                        url = left(url, i - 1) & cnum & mid(url, i + 3)
                                end if
                        end if
                end if
        next
        urlNormalize = url
end function


'-----------------------------------------------------------------------
' Main
'-----------------------------------------------------------------------
'

dim objWshell, objWnetwork
on error resume next ' putting "argc == 1 and" here doesn't work because of vb-"and"
if wscript.arguments(0) <> "register" and wscript.arguments(0) <> "unregister" _
                and left(wscript.arguments(0), 7) <> "file://" _
                and left(wscript.arguments(0), 6) <> "smb://" then
        wscript.echo _
                "Usage: " & wscript.scriptName & " URL/COMMAND" & vbcrlf & _
                "  URL must be an ""smb://""-style url" & vbcrlf & _
                "  COMMAND must be ""register"" or ""unregister""" & vbcrlf & _
                vbcrlf & _
                "If you're unsure about how to install this, follow these simple steps:" & vbcrlf & _
                " - copy this file to the ""C:\WINDOWS\"" directory" & vbcrlf & _
                " - start -> run -> type """ & wscript.scriptName & " register"" [enter]" & vbcrlf & _
		"    (without the quotes)"
        wscript.quit 1
end if
on error goto 0

set objWshell = wscript.createObject("wscript.shell")

if wscript.arguments(0) = "register" then
        objWshell.regWrite "HKEY_LOCAL_MACHINE\SOFTWARE\Classes\smb\", "URL:SMB URI"
        objWshell.regWrite "HKEY_LOCAL_MACHINE\SOFTWARE\Classes\smb\URL Protocol", "", "REG_SZ"
        objWshell.regWrite "HKEY_LOCAL_MACHINE\SOFTWARE\Classes\smb\Shell\Open\Command\", _
                """" & wscript.fullName & """ """ & wscript.scriptFullName & """ ""%l"""
        wscript.echo "Registered the smb:// protocol in the registry."
elseif wscript.arguments(0) = "unregister" then
        on error resume next
        objWshell.regDelete "HKEY_LOCAL_MACHINE\SOFTWARE\Classes\smb\Shell\Open\Command\"
        objWshell.regDelete "HKEY_LOCAL_MACHINE\SOFTWARE\Classes\smb\Shell\Open\"
        objWshell.regDelete "HKEY_LOCAL_MACHINE\SOFTWARE\Classes\smb\Shell\"
        objWshell.regDelete "HKEY_LOCAL_MACHINE\SOFTWARE\Classes\smb\URL Protocol"
        objWshell.regDelete "HKEY_LOCAL_MACHINE\SOFTWARE\Classes\smb\"
        on error goto 0
        wscript.echo "Removed the smb:// protocol from the registry."
else
        dim strFile, strComputer, pos
        if left(wscript.arguments(0), 4) = "file" then
                strFile = mid(wscript.arguments(0), 6)
        else
                strFile = mid(wscript.arguments(0), 5)
        end if
        ' Replace urlencoded (ONLY illegal and non-reserved) chars.
	' If the URLs you feed this contain spaces or percent signs, you MUST use percent-encoding.
        strFile = urlNormalize(strFile)
        ' Replace forward slash with backslash, for \\drive\share-notation
        strFile = replace(strFile, "/", "\")
        ' Escape double quotes
        strFile = replace(strFile, """", """""")
        ' If the computer asks for credentials, the ProtocolHandler will fail.
        ' Therefore we map the computer as a guest user first.
        pos = instr(3, strFile, "\")
        if pos = 0 then strComputer = strFile else strComputer = left(strFile, pos - 1)
        set objWnetwork = wscript.CreateObject("WScript.Network")
        ' Try to make a connection either way
        on error resume next
        objWnetwork.mapNetworkDrive "", strComputer, false, "guest"
        objWnetwork.mapNetworkDrive "", strComputer, false, "usernamethatdoesntexist"
        on error goto 0
        ' Now we can open the share(d file).. hopefully
        objWshell.run "rundll32.exe url.dll,FileProtocolHandler """ & strFile & """"
end if
