6

I use a lot of directory listing on my website and want to make .URL shortcut files into functional links. These .URL shortcuts were made by dragging the websites' icons from my Firefox URL bar to my Windows desktop.

The problem: Clicking a .URL item from a browser directory listing only displays the file's parameters (including the URL, see below) but does not load the website.

How can we make clicking a .URL item actually load the website?

I've searched every keyword combination I could think of but found no answers. The closest was a similar (but unanswered) question on StackOverflow: URL Shortcut in Web Directory

A further limitation is that I have shared Linux hosting from GoDaddy and thus don't have access to all the server setting "bells and whistles".


.URL Files

The Windows internet shortcut .URL file is an INI file of the following form, which contains the target URL in the URL key.

[InternetShortcut]
URL=https://eclipse2017.nasa.gov/sites/default/files/interactive_map/index.html
IDList=
HotKey=0
IconFile=C:\Users\Sunny\AppData\Local\Mozilla\Firefox\Profiles\ ... ==.ico
IconIndex=0

Live Example

In the directory: http://SoSaysSunny.com/eclipse_2017-08-21/
... I'd like the .URL items to be functional links.

  • How did you create that URL shortcut file (which appears to be an XML file that contains the actual URL that you'd like the user to be sent to). – MrWhite Jul 4 '17 at 8:55
  • @user82217 That is Windows functionality. You right click in file explorer and say "create URL link" or something like that. You then enter the link where you want it to redirect. When you double click on the file in Windows explorer you get taken to the URL. The contents of the file has the URL: [InternetShortcut] URL=https://eclipse2017.nasa.gov/sites/default/files/interactive_map/index.html It wouldn't surprise me if IIS handled these common windows URL shortcuts somehow. – Stephen Ostermiller Jul 4 '17 at 8:58
  • On ExpertsExchange (ick): How to Make .URL Files be Links from a Web Server but somebody actually has a solution where they have a CGI Perl script handle the URLs, read in the file contents, parse out the URL, and issue a redirect. – Stephen Ostermiller Jul 4 '17 at 9:05
  • @StephenOstermiller, I appreciate the edits to my original post to explain to the community. However, your changes altered the nature of my question. If I HAVE to do an Apache .htaccess hack, I will ... but it seems like there's got to be a shortcut file format that natively works. I can't be the only person too lazy to maintain an HTML list of links. ( :^Þ) – SoSaysSunny Jul 4 '17 at 9:30
  • 1
    @StephenOstermiller, Please take no offense. I'm old-school (coding HTML in WordPad since 1995 or so) and feel there must be a non-server-specific solution. Aren't there directory listing rendering standards or something that would address this issue? How did Gopher and other non-graphical interfaces do this back in the day? ... Or maybe I'm just ignorant & naive to think it should have been resolved in the first 20 yrs of the Internet. ~sigh~ – SoSaysSunny Jul 4 '17 at 9:58
3

I'm not aware of an Apache specific way to do this "automagically".

However, it's reasonably trivial to script this yourself. You can use the Apache module "mod_rewrite" (in .htaccess) to rewrite such requests to a PHP script that parses out the target URL to issue the redirect.

For example, in your root .htaccess file, include the following (mod_rewrite directives):

Options +Indexes

RewriteEngine On
RewriteBase /

# Rewrite requests for ".url" files to our PHP script
RewriteCond %{REQUEST_FILENAME} -f
RewriteRule .\.url$ process-url-shortcut.php [NS,NC,E=URL_FILE:%{REQUEST_FILENAME},L]

UPDATE: The NS flag is required if this is used together with Apache's generated directory listings - particularly if you are specifying custom icons (AddIcon) and descriptions (AddDescription) with Apache's FancyIndexing option (as above). This is to prevent the directive being processed during internal subrequests. See my answer to this follow up question for more information.

This rewrites all requests for URLs that end .url or .URL (not case-sensitive) that also map to existing files, to the PHP file /process-url-shortcut.php (in the document root) which ultimately handles the request. This also sets an environment variable, URL_FILE, that contains the full filesystem path of the file being requested. (Note that this becomes REDIRECT_URL_FILE after the internal rewrite - see the additional note below.)

Addtional... Note that the PHP script could exist anywhere, it doesn't need to go in the document root. (In fact, it would actually be preferable if this script was in a subdirectory elsewhere on the file system. The script is already passed an absolute file system path, so it doesn't matter where the script itself is located.) To locate the script somewhere else, you just need to update the RewriteRule substitution (ie. process-url-shortcut.php - the target URL). For example, if the script was located in a /scripts subdirectory off the document root, then you could change process-url-shortcut.php to read /scripts/process-url-shortcut.php instead - the slash prefix on the URL is optional in this example, because we have already specified this in the RewriteBase directive. Note that you must not specify an absolute URL here (ie. with a scheme + hostname) since this will implicitly trigger an external redirect, as opposed to an internal rewrite, which is not what we want.

The environment variable is used later by our PHP script. We could calculate the filename using PHP, however, this would take a few lines of code to parse the filename from the requested URL. It seemed easier and possibly more reliable to grab this with Apache's mod_rewrite instead, which already knows the absolute filename the original request mapped to (ie. our URL file). This also provides an easy way to block direct requests to our PHP script; if the environment variable is not set then the script is being requested directly, so block it. Two birds; one stone.

The PHP script /process-url-shortcut.php then handles the request:

<?php
// Check for environment variable that contains the URL file being requested
//  - This is set during the internal rewrite in .htaccess
$urlFile = getenv('REDIRECT_URL_FILE');
if ($urlFile) {

    # Read the contents of the URL file
    $urlFileData = file_get_contents($urlFile);
    if ($urlFileData) {

        # Extract just the target URL from the file
        if (preg_match('/url=(.+)/i',$urlFileData,$urlMatch)) {

            # If found a target URL in the file then redirect...
            if (isset($urlMatch[1])) {
                header('Location: '.$urlMatch[1],true,302);
                exit;
            }

        }

    }

}

// If got this far then script was accessed directly or something went wrong...
http_response_code(404);
?>
<h1>404 Not Found</h1>

This looks for the first url=<something> in the requested file (not case-sensitive), using a regex. And extracts <something> to be used in the HTTP redirect.

Note that this contains only the bare minimum validation. You could check that the target URL looks like a URL, limit the target URLs to certain domains, restrict the directories that get parsed for .url files. etc. etc.


I was going to use PHP's parse_ini_file() to extract the URL key value, however, the example "INI file" given in the question does not appear to conform to the specification PHP is expecting:

Warning: syntax error, unexpected '=' in ... on line 5

Which appears to relate to the == that appears later on the IconFile line.


Additional Notes...

The php "REDIRECT_URL_FILE" doesn't match the .htaccess "E=URL_FILE"

Yes, that is correct. This is indeed a bit confusing (especially if you are not familiar with how the rewrite engine works). Whilst we set the environment variable URL_FILE at the time of the URL rewrite, Apache renames this to REDIRECT_URL_FILE after the rewrite occurs. In fact, all environment variables that existed prior to the rewrite are prefixed with REDIRECT_ after the rewrite (that is, after this pass through the rewrite process, not literally just after that directive).

Also: I'm trying to make web links (e.g. nasa.gov), not "full filesystem path" links, if that makes a difference.

The "full filesystem path" that I refer to in my answer is just the "filesystem path" of the actual file being requested, ie. the .URL file you have saved to that directory. The "full filesystem path" is simply required so that the PHP script is able to open and read this file. Yes, this ultimately creates "web links" to the URL that is specified within the shortcut file.

  • 1
    Thank you for this solution. I'm going to have to dig into what you said and try it out. I felt certain there was a way to do this (especially in a non-server-specific way). – SoSaysSunny Jul 5 '17 at 19:48
  • 1
    I've created a chat room to discuss this script and copied/answered your above comments in the chat room. (You need 100+ rep points to be able to create a chat room.) – MrWhite Jul 7 '17 at 11:01
  • 1
    Your solution worked perfectly ... once I followed the instructions precisely. The .htaccess file with the code you gave MUST be placed in the root directory (modified by any .htaccess files in various subdirectories) and the PHP script MUST also be placed in the root directory. NOTE: This solution requires no special privileges on the server and thus is perfect for sites on shared hosting, such as GoDaddy. Thank you so much for all your help. – SoSaysSunny Jul 8 '17 at 18:16
  • The .htaccess directives above can be placed in any .htaccess file along the file system path, they don't need to go in the .htaccess file in the root directory. You would place these directives in a subdirectory to restrict their influence to just that directory tree, rather than the entire filesystem (although this can also be controlled from within the root .htaccess). Likewise, the PHP script can also be placed anywhere - you just need to update the file path of where the script is located. (I've updated my answer.) – MrWhite Aug 4 '17 at 12:05
1

Putting my solution here for future reference:

Step 1 Drop this in your .htaccess* (this works even in subfolders; I'm using it on a shared hosting package without administrative access, too):

AddType text/x-url url
Action text/x-url /cgi-bin/url.cgi

Step 2 Paste this into ${cgi_bin}/url.cgi:

#!/bin/sh

while read -r line; do
  case "${line}" in
   "URL="*|"http"*)
      url="${line#"URL="}"
      status="302 Found"
      content_type="text/x-url"
      break
      ;;
  esac
done < "${PATH_TRANSLATED="${1}"}"

exec printf "Status: %s\r\n${content_type+"Content-Type: %s\\r\\n"}${url+"Location: %s\\r\\n"}\r\n"\
 "${status?"500 Internal Server Error"}"\
 ${content_type+"${content_type}"}\
 ${url:+"${url}"}

exec cat "${PATH_TRANSLATED}"

(Remove the first exec if you want to have the option to download the file—however, note that this will cause problems in some mobile devices.)

Step 3 You're done! Uploaded .url files are now hyperlinks within a directory listing.


* If you are using Hiawatha, this script also works as a pseudo "CGI Handler":

CGIHandler=/var/www/cgi-bin/url.cgi:url

(Don't forget to configure CGIWrapper, if your server uses it.)

  • This script's readability may be coming at a price. This makes me wonder 3 things: 1. Does it perform worse than @MrWhite's urlFileData|preg_match PHP solution? (By what factor?) 2. If PHP is more performant for this task, could it be leveraged via the more idiomatic, more succinct, more readable, less invasive mod_actions? 3. In a high-performance context, would sed or awk be more appropriate at this task? – JamesTheAwesomeDude Jun 5 '18 at 14:50

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.