Nathan Grigg

Creating private links to files using Python

For a while I’ve been wanting to create a private link system. Google Docs, Dropbox, YouTube, and others all give you the option to make a file public but “unlisted,” with a long link that no one will likely guess. You can email the link to others, and no one has to worry about usernames or passwords. This week I implemented a rudimentary system as a Python cgi script.

Schematic

Each file is assigned an id. The ids and corresponding filenames are stored in a text file. When a user requests and id, the Python script checks if the id is in the table, and, if so, serves up the appropriate file. If the id does not have a corresponding file, the user gets an error message.

The id

You can use anything you want here, really. I use a 10-byte id encoded in base 32 as a length-16 string. You could really use a shorter id and still be okay. The nice thing about base 32 is that it is URL safe, and it doesn’t use 0’s, 1’s or 8’s, to avoid confusion with O’s, I’s, and B’s. You can generate an id using the following code:

import os,base64
id = base64.b32encode(os.urandom(10))

I store the ids in a text file that looks something like this

NRTDBP5QYKN3WGYP some-file.pdf
WMADW3QOSHSCATWY another-file.pdf
UEGGUKOMB5FXWNR2 a third file.pdf

Serving up the file

As with any cgi script, you just need to print everything to stdout, starting with the headers. The headers I want to use are

Content-Type: application/pdf;
Content-Disposition: inline; filename="name of file.pdf";
Content-Length: (size of file in bytes);

You can replace “inline” with “attachment” if you want the browser to download the file instead of displaying it in the browser. Don’t forget the quotes around the file name if it has any spaces or special characters in it. Also, don’t forget to send a blank line after the headers and before sending the content. Then you finish it off with

print file.read()

The script is here: private-link.py

A little mod_rewrite

So far, the user needs to enter a URL in the form http://example.com/?id=NRTDBP. With the help of mod_rewrite, we can accept URLs like http://example.com/NRTDBP. Here is the relevant .htaccess file, taking into account that the Python script is named index.cgi.

RewriteEngine On
RewriteBase /path/to/folder/
RewriteRule ^index.cgi - [L]
RewriteRule ^([A-Z0-9a-z]+)/?$ index\.cgi?id=$1 [L]

If you are confused about the last line, here some help on regular expressions.