'-----------------------------------------------------------------------
'  smburl3b.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!
'
' 20080922 smburl3b:
'   Browsers nowadays assume that unicode in URLs must be in UTF-8. I
'   added decoding of UTF-8 urlencoded entities.
'   E.g. %C3%A5 becomes "a-ring".
'   
'-----------------------------------------------------------------------
' 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, j, cnum, utf8len, tmp
    rfc3986reserved = "!*'();:@&=+$,/?#[]" ' "%" (percent) is removed from this list
    i = 1
    do while i + 2 <= len(url)
        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
		' We have an urlencoded entity. The probability that it is UTF-8
		' encoded, is high.
		if (cnum and &h80) = 0 then
		    utf8len = 0
		elseif (cnum and &he0) = &hc0 then
		    utf8len = 1
		    cnum = cnum and &h1f
		elseif (cnum and &hf0) = &he0 then
		    utf8len = 2
		    cnum = cnum and &h0f
		elseif (cnum and &hf8) = &hf0 then
		    utf8len = 3
		    cnum = cnum and &h07
		end if
		for j = 3 to utf8len * 3 step 3
		    if i + j + 2 > len(url) then exit for ' too short
		    tmp = null
		    if mid(url, i + j, 1) = "%" then
			tmp = chr0x(mid(url, i + j + 1, 1)) * 16 + chr0x(mid(url, i + j + 2, 1))
			if not isnull(tmp) then
			    cnum = cnum * 64 ' << 6
			    cnum = cnum or (tmp and &h3f)
			end if
		    end if
		    if isnull(tmp) then exit for ' bad UTF-8
                next
		cnum = chr(cnum)
		if instr(rfc3986reserved, cnum) = 0 then
		    url = left(url, i - 1) & cnum & mid(url, i + j)
		end if
            end if
        end if
	i = i + 1
    loop
    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

' vim: set ts=8 sts=4 sw=4 et
